-
Notifications
You must be signed in to change notification settings - Fork 2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Feature Request] Resolver Middleware #1516
Comments
@dereklavigne18 That sounds great 👍 One important note is that we should be extremely careful not to add a performance penalty, especially for big queries that resolved asynchronously. See this discussion in #723. |
+1 to @dereklavigne18. Custom error handling and authorization middlewares would save so much time. |
What if it was more event oriented? As in, instead of having a middleware sort of thing, we can have an event emitter api of sorts, and have a couple of options like:
Later on we can just add more event handlers which can be plugged into. |
I don't think this is a good suggestion. It definitely will make graphql resolvers to be more complicated than they should be. As an alternative approach, you can define your resolvers as async function composition (or something like in koa pipeline). Each function is a middleware. Very likely all top level resolvers will have the same stages. For example: // stages for loading logic
[
'authenticate',
'load',
'checkIfFound',
'authorize',
'respond'
] Later you may create a function function which can return different pipelines for different situations. Lets say that when we want to load a list of objects we have const Query = {
users: loadCollection('User') // User is a model name it can be any model name from your models,
user: loadResource('User'),
login: loadResource('User', {
authenticate: false, // lets make login query to be publicly accessible
load: actions.loadUserByCredentials, // overwrite load stage
}),
}
const Mutation = {
createUser: mutateResource('create', 'User', {
validate: ... // custom validation function
})
} You may have different stages (with default implementations, aka middlewares) for different types of Queries and Mutations (or you can customize default implementation by passing an object with stage names) The good point about factory pipeline is that eventually your code is
This pattern is very similar to Command pattern from Domain Driven Design. And this is what I used with GraphQL during migration from Express. |
Note that there is already a middleware implementation in GraphQL-core (see middleware.py and test_middleware) where you can steal ideas from. |
We wrote this naive implementation of middlewares here if you are still interested: https://github.com/unirakun/graphql-directives-middlewares The solution given by @stalniy seems quite good to me tho! |
I think we can solve this in userland instead |
@sibelius The community already has |
I don’t think this should be in the graphql-js core package |
I've been using an approach like this: https://github.com/JCMais/graphql-yup-middleware |
As @sibelius said and me and @danielrearden pointed, we already have a solution for that out of graphql-js. |
I'd like to see resolvers becoming full-fledged middleware. It seems currently resolvers are almost like middleware, but limited in that a parent can only run code before but not after the child resolvers. Hence the parent resolver can not modify the child resolver responses and can't handle their errors. One use case is handling resolver errors in the parent resolver. For example, if a certain error was thrown in a child resolver, the parent resolver could decide to retry the whole child resolver chain again. This is currently not achievable with user-land middleware. Likely graphql-js would need to change it's validation model, since parent resolvers could now change the return values from child resolvers and handle their errors. It could validate the response of a resolver and throw an |
Closing this issue as solved in user land. In terms of retrying on error, that functionality seems at first glance counter to the specification — there may be an algorithmic equivalence that makes it neutral, but it would still seem to be best done outside the reference implementation. |
The approach that @the-guild-org is taking and recommending for middleware and plugins for graphql-js is the approach of the Envelop plugin system. It is also the core of our GraphQL Yoga Plugin system and our Hive Gateway Plugin system. https://the-guild.dev/graphql/envelop/docs/plugins/lifecycle |
I'm interested to hear how others feel about the idea of using middleware installed on resolver functions to allow all resolvers in a schema to perform common functionality.
As my schema grows I've found more and more of a need to add some of the same logic to all my resolvers. It would be nice if we could add some middleware that would be executed on resolvers in the schema when we go to execute the query. This could allow users to implement things like custom error handling, logging, and authorization on all resolvers without the need to add them to each resolver on the schema.
The text was updated successfully, but these errors were encountered: