My feature request is partially related to #1627. Generally, I want to integrate with a graphql-query-complexity library that creates validationRules but it need access to the query variables values and they're not present in ValidationContext in graphql-js. Here's how it's configured using plain express-graphql middleware:
import queryComplexity from 'graphql-query-complexity';
import express from 'express';
import graphqlHTTP from 'express-graphql';
import schema from './schema';
const app = express();
app.use('/api', graphqlHTTP(async (request, response, {variables}) => ({
schema,
validationRules: [ queryComplexity({
maximumComplexity: 1000,
variables,
onComplete: (complexity: number) => {console.log('Query Complexity:', complexity);},
}) ]
})));
In graphql-yoga there is a callback, like in apollo-server for context:
https://github.com/prisma/graphql-yoga/blob/3362b13374190fd2390594495dc08064e5ee4815/src/types.ts#L64-L77
And I think that apollo-server should support this kind of configuration too 馃槈 Changing ValidationContext mechanism in graphql-js is much more complicated and the only way that the author has figured out is by making validationRules dynamic in Apollo Server:
https://github.com/slicknode/graphql-query-complexity/issues/7#issuecomment-427435851
It would be great if the variables could be accessible as a callback.
I had some luck using the "old way" of accessing request headers (https://www.apollographql.com/docs/apollo-server/migration-two-dot.html#request-headers), then passing variables: req.body.variables to queryComplexity. However it fell short on batched queries.
Closing this because validationRules shouldn't have access to query variables, because validation isn't related to any particular operation and isn't guaranteed to run on every request (as an optimization, validation only happens once per operation and is skipped on subsequent requests).
@martijnwalraven so what do you propose instead or complexity calculation for preventing DDoS of GraphQL API?
@19majkel94 Complexity calculation is a fine solution, but using the didResolveOperation hook from the request pipeline plugin API would be a better place for this because it gets called for every request and has access both to the specific operation that gets executed and the query variables.
@martijnwalraven
Thanks for the tip! Looks like it works like a charm 馃槃
But is there a better way to receive a operation DocumentNode rather than:
import { parse } from "graphql";
// ...
didResolveOperation({ request, operation }) {
const operationString = request.query!.substring(
operation.loc!.start,
operation.loc!.end,
);
const query = parse(operationString);
},
@19majkel94 Good to hear! Yep, you should have access to document. Note that a document may contain multiple operations however, so you may also want to take operation or operationName into account.
Oh, and your current code wouldn't work with fragments, because those are not part of operation.
I can't use just document because it may contains not executed operations and thus overcalculating complexity.
So how to proper cut document into operation with fragments as DocumentNode?
You should be able to use separateOperations from graphql-js for that.
Great! Thanks! 馃槂
I was looking under extractOperations key 馃槃
So here is the (I hope) final version:
const query = request.operationName
? separateOperations(document)[request.operationName]
: document;
That looks great!
Here is a working example of graphql-query-complexity integration with Apollo Server 馃挭
https://github.com/19majkel94/type-graphql/blob/4501867fffe3e6f5b3e71af0b71651efcd48d9c3/examples/query-complexity/index.ts#L16-L64
https://github.com/apollographql/apollo-server/issues/1777#issuecomment-509376077
Closing this because validationRules shouldn't have access to query variables, because validation isn't related to any particular operation and isn't guaranteed to run on every request (as an optimization, validation only happens once per operation and is skipped on subsequent requests).
this consumed a significant amount of our time because we assumed it ran on every request, from some clients we permit certain queries and in others we do not. could the documentation be updated to include that please https://www.apollographql.com/docs/apollo-server/api/apollo-server/#validationrules ?
@rex-remind101 I'm sorry you were confused by our docs here! Yeah, the purpose of validation rules is to be a pure function of "operation + schema" so it doesn't work with other things. Those docs can be found at docs/source/api/apollo-server.md. I bet you have a better idea of what would have been clear to you than I would, so want to give a PR a try?
Most helpful comment
Here is a working example of
graphql-query-complexityintegration with Apollo Server 馃挭https://github.com/19majkel94/type-graphql/blob/4501867fffe3e6f5b3e71af0b71651efcd48d9c3/examples/query-complexity/index.ts#L16-L64