How to correctly propagate errors from plugins?
See original GitHub issueI’m writing an Apollo Server plugin that validates a presence of a certain HTTP header. It looks like this:
import { ApolloServerPlugin } from 'apollo-server-plugin-base';
import { OperationDefinitionNode } from 'graphql';
export const myPlugin: ApolloServerPlugin = {
requestDidStart() {
return {
didResolveOperation(context) {
if (!headerIsValid(context.request.http!.headers.get('x-special-header'))) {
throw new Error('header not valid');
}
},
};
},
};
I’m doing that in a plugin because I need access to a parsed query so that I can distinguish introspection queries and normal queries, and I didn’t find a better place to do it, see here.
Previously, I had this logic in the context: () => {...}
function inside new ApolloServer
constructor and when I threw the error there, it was returned to the client and not logged to console.
When I throw an error in a plugin, it is sent to the client but also logged to a console, as if it was an uncaught error.
Am I doing it correctly? Is there a way to avoid having a full stack trace in the server console / logs? My code does not have any error, I just want to indicate a problematic query to the user.
Issue Analytics
- State:
- Created 4 years ago
- Reactions:1
- Comments:12 (3 by maintainers)
Top GitHub Comments
To give a concrete followup to anyone stumbling on this issue, here’s what I’ve learned.
Throwing an error from inside the plugin methods will cause
didEncounterErrors
to fire. The only plugin method which lets you actually send a response isresponseForOperation
. There are two ways, therefore, to modify the response:responseForOperation
context.response
.If you follow the code, you’ll find that before issuing the HTTP response, the Apollo code checks to see if
erorrs.length
is non-zero and thatdata
is null. If this is the case, it responds with a 400 status code and an object called “error” which contains the errors. Essentially, it doesn’t know what to do with it at that point.In fact, returning from
responseForOperation
with errors, but nodata
object, will cause the above to happen as well.To address my problem, I am returning the a properly spec’d GQl response, including
errors
anddata
with null values for each ofcontext.operation.selectionSet.selections
. This makes the response mimic what would be expected when throwing any error inside of resolvers. This special case exists because we want to stop execution from inside of a plugin.@borekb I am struggling with a similar problem. In my case, when I throw an error from a plugin lifecycle method, the value returned to the client is
Which is not up to spec AFAIK and should instead be
Have you seen this behavior too as it pertains to error handling in plugins?