GraphQLSwift / Graphiti

The Swift GraphQL Schema framework for macOS and Linux
MIT License
531 stars 67 forks source link

Different contexts for different operation types #135

Closed rexmas closed 6 months ago

rexmas commented 6 months ago

It's typical that queries, mutations, and subscriptions all have different dependencies and permissions (such as read vs read/write vs web socket) and it's therefore justified to give them different contexts. Currently I've gotten stuck on finding a way to effectively do this.

With https://github.com/async-graphql/async-graphql this is more or less doable because there is a singular context that holds the user's position in the AST within it so it's possible to retrieve operation type early in execution and pattern match to manipulate the context. Such a solution would imply forcing everyone to use the same context, which can also carry their own specialized context, but this may not go over well given precedent. Another possibility is a context middle-ware that can be provided to the schema, and allows context manipulation for each operation type before full execution.

I also was considering a macro that could be added to just manipulate the context if the resolver comes from a Query or Mutation or Subscription, unfortunately the resolvers are all coupled in the type definition of Schema:

public final class Schema<Resolver, Context>

Any thoughts on how we can accomplish this?

NeedleInAJayStack commented 6 months ago

GraphQLRequest has an operationType method. In my work, I've used this method to pre-determine the operation requested and change functionality based on that (like database transactioning/websocket stuff/etc). However, I haven't done any adjustment of the Context based on the operation.

The Schema API only allows for a single Context type across all operations, but it seems like you could use the type-system to provide varying functionality. For example, you could use a Context protocol in the schema definition, conform QueryContext, MutationContext, etc to that protocol and add operation-specific needs, and then typecast in your resolvers when you need those operation-specific features. Or use subclassing in the same way.

rexmas commented 6 months ago

Ya, that's more or less what I went with but was hoping to avoid a solution that requires specification on a per resolver basis...

NeedleInAJayStack commented 6 months ago

Yeah, unfortunately this package doesn't currently support middlewares at the moment. We're always open to MRs though! in the meantime, I'm going to close this issue - feel free to reopen if you feel it is necessary.