diff --git a/README.md b/README.md index ffdf3da..3b4fb18 100644 --- a/README.md +++ b/README.md @@ -87,3 +87,18 @@ Response: ``` See the `RouteBuilder.graphql` function documentation for advanced configuration options. + +### Computing GraphQL Context + +The required closure in the `graphql` function is used to compute the `GraphQLContext` object, which is injected into each GraphQL resolver. The `inputs` argument passes in data from the request so that the Context can be created dynamically: + +```swift +app.graphql(schema: schema) { inputs in + return GraphQLContext( + userID: inputs.vaporRequest.auth.userID, + logger: inputs.vaporRequest.logger, + debug: inputs.vaporRequest.headers[.init("debug")!] != nil, + operationName: inputs.graphQLRequest.operationName + ) +} +``` diff --git a/Sources/GraphQLVapor/GraphQLHandler.swift b/Sources/GraphQLVapor/GraphQLHandler.swift index 3655ed7..16b28d6 100644 --- a/Sources/GraphQLVapor/GraphQLHandler.swift +++ b/Sources/GraphQLVapor/GraphQLHandler.swift @@ -8,5 +8,5 @@ struct GraphQLHandler< let schema: GraphQLSchema let rootValue: any Sendable let config: GraphQLConfig - let computeContext: @Sendable (Request) async throws -> Context + let computeContext: @Sendable (GraphQLContextComputationInputs) async throws -> Context } diff --git a/Sources/GraphQLVapor/HTTP/GraphQLHandler+HTTP.swift b/Sources/GraphQLVapor/HTTP/GraphQLHandler+HTTP.swift index 0b3dc53..fa0262b 100644 --- a/Sources/GraphQLVapor/HTTP/GraphQLHandler+HTTP.swift +++ b/Sources/GraphQLVapor/HTTP/GraphQLHandler+HTTP.swift @@ -15,7 +15,11 @@ extension GraphQLHandler { guard operationType != .mutation else { throw Abort(.methodNotAllowed, reason: "Mutations using GET are disallowed") } - let context = try await computeContext(request) + let graphQLContextComputationInputs = GraphQLContextComputationInputs( + vaporRequest: request, + graphQLRequest: graphQLRequest + ) + let context = try await computeContext(graphQLContextComputationInputs) let result = await execute( graphQLRequest: graphQLRequest, context: context, @@ -30,7 +34,11 @@ extension GraphQLHandler { throw Abort(.unsupportedMediaType, reason: "Missing `Content-Type` header") } let graphQLRequest = try request.content.decode(GraphQLRequest.self) - let context = try await computeContext(request) + let graphQLContextComputationInputs = GraphQLContextComputationInputs( + vaporRequest: request, + graphQLRequest: graphQLRequest + ) + let context = try await computeContext(graphQLContextComputationInputs) let result = await execute( graphQLRequest: graphQLRequest, context: context, diff --git a/Sources/GraphQLVapor/RoutesBuilder+graphql.swift b/Sources/GraphQLVapor/RoutesBuilder+graphql.swift index 3033a48..43dcd8d 100644 --- a/Sources/GraphQLVapor/RoutesBuilder+graphql.swift +++ b/Sources/GraphQLVapor/RoutesBuilder+graphql.swift @@ -26,7 +26,7 @@ public extension RoutesBuilder { schema: GraphQLSchema, rootValue: any Sendable = (), config: GraphQLConfig = GraphQLConfig(), - computeContext: @Sendable @escaping (Request) async throws -> Context + computeContext: @Sendable @escaping (GraphQLContextComputationInputs) async throws -> Context ) { ContentConfiguration.global.use(encoder: GraphQLJSONEncoder(), for: .jsonGraphQL) ContentConfiguration.global.use(decoder: JSONDecoder(), for: .jsonGraphQL) @@ -67,3 +67,8 @@ public extension RoutesBuilder { } } } + +public struct GraphQLContextComputationInputs: Sendable { + public let vaporRequest: Request + public let graphQLRequest: GraphQLRequest +} diff --git a/Sources/GraphQLVapor/WebSocket/GraphQLHandler+handleWebSocket.swift b/Sources/GraphQLVapor/WebSocket/GraphQLHandler+handleWebSocket.swift index 20a4499..583c56c 100644 --- a/Sources/GraphQLVapor/WebSocket/GraphQLHandler+handleWebSocket.swift +++ b/Sources/GraphQLVapor/WebSocket/GraphQLHandler+handleWebSocket.swift @@ -9,7 +9,6 @@ extension GraphQLHandler { request: Request ) async throws -> Response { let subProtocol = try negotiateSubProtocol(request: request) - let context = try await computeContext(request) let response = Response(status: .switchingProtocols) response.upgrader = WebSocketUpgrader( maxFrameSize: .default, @@ -26,7 +25,12 @@ extension GraphQLHandler { let server = GraphQLTransportWS.Server>( messenger: messenger, onExecute: { graphQLRequest in - try await graphql( + let graphQLContextComputationInputs = GraphQLContextComputationInputs( + vaporRequest: request, + graphQLRequest: graphQLRequest + ) + let context = try await computeContext(graphQLContextComputationInputs) + return try await graphql( schema: schema, request: graphQLRequest.query, rootValue: rootValue, @@ -36,7 +40,12 @@ extension GraphQLHandler { ) }, onSubscribe: { graphQLRequest in - try await graphqlSubscribe( + let graphQLContextComputationInputs = GraphQLContextComputationInputs( + vaporRequest: request, + graphQLRequest: graphQLRequest + ) + let context = try await computeContext(graphQLContextComputationInputs) + return try await graphqlSubscribe( schema: schema, request: graphQLRequest.query, rootValue: rootValue, @@ -52,7 +61,12 @@ extension GraphQLHandler { let server = GraphQLWS.Server>( messenger: messenger, onExecute: { graphQLRequest in - try await graphql( + let graphQLContextComputationInputs = GraphQLContextComputationInputs( + vaporRequest: request, + graphQLRequest: graphQLRequest + ) + let context = try await computeContext(graphQLContextComputationInputs) + return try await graphql( schema: schema, request: graphQLRequest.query, rootValue: rootValue, @@ -62,7 +76,12 @@ extension GraphQLHandler { ) }, onSubscribe: { graphQLRequest in - try await graphqlSubscribe( + let graphQLContextComputationInputs = GraphQLContextComputationInputs( + vaporRequest: request, + graphQLRequest: graphQLRequest + ) + let context = try await computeContext(graphQLContextComputationInputs) + return try await graphqlSubscribe( schema: schema, request: graphQLRequest.query, rootValue: rootValue,