Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Examples/HelloWorld/Sources/HelloWorld/HelloWorld.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,11 @@ struct HelloWorld {
)

let router = Router(context: HummingbirdContext.self)
router.graphql(schema: schema, config: .init(allowMissingAcceptHeader: true)) { _, _ in
router.graphql(schema: schema, config: .init(allowMissingAcceptHeader: true)) { _ in
GraphQLContext()
}
let webSocketRouter = Router(context: HummingbirdWebSocketContext.self)
webSocketRouter.graphqlWebSocket(schema: schema) { _, _ in
webSocketRouter.graphqlWebSocket(schema: schema) { _ in
GraphQLContext()
}
let app = Application(
Expand Down
21 changes: 18 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ struct GraphQLContext: Sendable {}

// Create router and register GraphQL
let router = Router()
router.graphql(schema: schema) { _, _ in
router.graphql(schema: schema) { _ in
return GraphQLContext()
}

Expand Down Expand Up @@ -98,6 +98,21 @@ Response:

See the `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
router.graphql(schema: schema) { inputs in
return GraphQLContext(
userID: inputs.hummingbirdContext.userID,
logger: inputs.hummingbirdContext.logger,
debug: inputs.hummingbirdRequest.headers[.init("debug")!] != nil,
operationName: inputs.graphQLRequest.operationName
)
}
```

### WebSockets

Subscription support via WebSockets can be enabled by calling the `graphqlWebSocket` function on a `Router` whose context conforms to `WebSocketRequestContext`, from the `HummingbirdWebSocket` package:
Expand All @@ -113,7 +128,7 @@ struct MyWebSocketContext: WebSocketRequestContext, RequestContext {
}

let router = Router(context: MyContext.self)
router.graphql(schema: schema) { _, _ in
router.graphql(schema: schema) { _ in
GraphQLContext()
}
let webSocketRouter = Router(context: MyWebSocketContext.self)
Expand Down Expand Up @@ -143,7 +158,7 @@ router.graphql(
config: .init(
coders: .init(graphQLJSONEncoder: graphQLJSONEncoder)
)
) { _, _ in
) { _ in
GraphQLContext()
}
```
Expand Down
10 changes: 9 additions & 1 deletion Sources/GraphQLHummingbird/GraphQLHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,13 @@ struct GraphQLHandler<
let schema: GraphQLSchema
let rootValue: any Sendable
let config: GraphQLConfig<WebSocketInit>
let computeContext: @Sendable (Request, Context) async throws -> GraphQLContext
let computeContext: @Sendable (GraphQLContextComputationInputs<Context>) async throws -> GraphQLContext
}

public struct GraphQLContextComputationInputs<
Context: RequestContext
>: Sendable {
public let hummingbirdRequest: Request
public let hummingbirdContext: Context
public let graphQLRequest: GraphQLRequest
}
18 changes: 14 additions & 4 deletions Sources/GraphQLHummingbird/HTTP/GraphQLHandler+HTTP.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,15 @@ extension GraphQLHandler {
guard operationType != .mutation else {
throw HTTPError(.methodNotAllowed, message: "Mutations using GET are disallowed")
}
let graphqlContext = try await computeContext(request, context)
let graphQLContextComputationInputs = GraphQLContextComputationInputs<Context>(
hummingbirdRequest: request,
hummingbirdContext: context,
graphQLRequest: graphQLRequest
)
let graphQLContext = try await computeContext(graphQLContextComputationInputs)
let result = await execute(
graphQLRequest: graphQLRequest,
context: graphqlContext,
context: graphQLContext,
additionalValidationRules: config.additionalValidationRules
)
return try encodeResponse(result: result, request: request, context: context)
Expand Down Expand Up @@ -58,10 +63,15 @@ extension GraphQLHandler {
throw HTTPError(.unsupportedMediaType)
}

let graphqlContext = try await computeContext(request, context)
let graphQLContextComputationInputs = GraphQLContextComputationInputs<Context>(
hummingbirdRequest: request,
hummingbirdContext: context,
graphQLRequest: graphQLRequest
)
let graphQLContext = try await computeContext(graphQLContextComputationInputs)
let result = await execute(
graphQLRequest: graphQLRequest,
context: graphqlContext,
context: graphQLContext,
additionalValidationRules: config.additionalValidationRules
)
return try encodeResponse(result: result, request: request, context: context)
Expand Down
7 changes: 3 additions & 4 deletions Sources/GraphQLHummingbird/Router+graphql.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public extension RouterMethods {
schema: GraphQLSchema,
rootValue: any Sendable = (),
config: GraphQLConfig<EmptyWebSocketInit> = .init(),
computeContext: @Sendable @escaping (Request, Context) async throws -> GraphQLContext
computeContext: @Sendable @escaping (GraphQLContextComputationInputs<Context>) async throws -> GraphQLContext
) -> Self {
// https://github.com/graphql/graphql-over-http/blob/main/spec/GraphQLOverHTTP.md#request
let handler = GraphQLHandler<Context, GraphQLContext, EmptyWebSocketInit>(
Expand Down Expand Up @@ -84,7 +84,7 @@ public extension RouterMethods where Context: WebSocketRequestContext {
schema: GraphQLSchema,
rootValue: any Sendable = (),
config: GraphQLConfig<WebSocketInit> = GraphQLConfig<EmptyWebSocketInit>(),
computeContext: @Sendable @escaping (Request, Context) async throws -> GraphQLContext
computeContext: @Sendable @escaping (GraphQLContextComputationInputs<Context>) async throws -> GraphQLContext
) -> Self {
let handler = GraphQLHandler<Context, GraphQLContext, WebSocketInit>(
schema: schema,
Expand All @@ -96,12 +96,11 @@ public extension RouterMethods where Context: WebSocketRequestContext {
ws(path, shouldUpgrade: { request, _ in
try handler.shouldUpgrade(request: request)
}) { inbound, outbound, context in
let graphQLContext = try await computeContext(context.request, context.requestContext)
let subProtocol = try handler.negotiateSubProtocol(request: context.request)
try await handler.handleWebSocket(
inbound: inbound,
outbound: outbound,
graphqlContext: graphQLContext,
context: context,
subProtocol: subProtocol,
logger: context.logger
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ import Hummingbird
import HummingbirdWebSocket
import Logging

extension GraphQLHandler {
extension GraphQLHandler where Context: WebSocketRequestContext {
func handleWebSocket(
inbound: WebSocketInboundStream,
outbound: WebSocketOutboundWriter,
graphqlContext: GraphQLContext,
context: WebSocketRouterContext<Context>,
subProtocol: WebSocketSubProtocol,
logger: Logger
) async throws {
Expand All @@ -22,21 +22,33 @@ extension GraphQLHandler {
let server = GraphQLTransportWS.Server<WebSocketInit, AsyncThrowingStream<GraphQLResult, Error>>(
messenger: messenger,
onExecute: { graphQLRequest in
try await graphql(
let graphQLContextComputationInputs = GraphQLContextComputationInputs<Context>(
hummingbirdRequest: context.request,
hummingbirdContext: context.requestContext,
graphQLRequest: graphQLRequest
)
let graphQLContext = try await computeContext(graphQLContextComputationInputs)
return try await graphql(
schema: self.schema,
request: graphQLRequest.query,
rootValue: self.rootValue,
context: graphqlContext,
context: graphQLContext,
variableValues: graphQLRequest.variables,
operationName: graphQLRequest.operationName
)
},
onSubscribe: { graphQLRequest in
try await graphqlSubscribe(
let graphQLContextComputationInputs = GraphQLContextComputationInputs<Context>(
hummingbirdRequest: context.request,
hummingbirdContext: context.requestContext,
graphQLRequest: graphQLRequest
)
let graphQLContext = try await computeContext(graphQLContextComputationInputs)
return try await graphqlSubscribe(
schema: self.schema,
request: graphQLRequest.query,
rootValue: self.rootValue,
context: graphqlContext,
context: graphQLContext,
variableValues: graphQLRequest.variables,
operationName: graphQLRequest.operationName
).get()
Expand All @@ -51,21 +63,33 @@ extension GraphQLHandler {
let server = GraphQLWS.Server<WebSocketInit, AsyncThrowingStream<GraphQLResult, Error>>(
messenger: messenger,
onExecute: { graphQLRequest in
try await graphql(
let graphQLContextComputationInputs = GraphQLContextComputationInputs<Context>(
hummingbirdRequest: context.request,
hummingbirdContext: context.requestContext,
graphQLRequest: graphQLRequest
)
let graphQLContext = try await computeContext(graphQLContextComputationInputs)
return try await graphql(
schema: self.schema,
request: graphQLRequest.query,
rootValue: self.rootValue,
context: graphqlContext,
context: graphQLContext,
variableValues: graphQLRequest.variables,
operationName: graphQLRequest.operationName
)
},
onSubscribe: { graphQLRequest in
try await graphqlSubscribe(
let graphQLContextComputationInputs = GraphQLContextComputationInputs<Context>(
hummingbirdRequest: context.request,
hummingbirdContext: context.requestContext,
graphQLRequest: graphQLRequest
)
let graphQLContext = try await computeContext(graphQLContextComputationInputs)
return try await graphqlSubscribe(
schema: self.schema,
request: graphQLRequest.query,
rootValue: self.rootValue,
context: graphqlContext,
context: graphQLContext,
variableValues: graphQLRequest.variables,
operationName: graphQLRequest.operationName
).get()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ struct HTTPStatusCodeGraphQLJSONTests {
@Test func parsingFailureGivesBadRequest() async throws {
// https://github.com/graphql/graphql-over-http/blob/main/spec/GraphQLOverHTTP.md#json-parsing-failure-1
let router = Router()
router.graphql(schema: helloWorldSchema) { _, _ in
router.graphql(schema: helloWorldSchema) { _ in
EmptyContext()
}
let app = Application(router: router)
Expand All @@ -36,7 +36,7 @@ struct HTTPStatusCodeGraphQLJSONTests {
@Test func invalidParametersGivesBadRequest() async throws {
// https://github.com/graphql/graphql-over-http/blob/main/spec/GraphQLOverHTTP.md#invalid-parameters-1
let router = Router()
router.graphql(schema: helloWorldSchema) { _, _ in
router.graphql(schema: helloWorldSchema) { _ in
EmptyContext()
}
let app = Application(router: router)
Expand All @@ -56,7 +56,7 @@ struct HTTPStatusCodeGraphQLJSONTests {
@Test func documentParsingFailureGivesBadRequest() async throws {
// https://github.com/graphql/graphql-over-http/blob/main/spec/GraphQLOverHTTP.md#document-parsing-failure-1
let router = Router()
router.graphql(schema: helloWorldSchema) { _, _ in
router.graphql(schema: helloWorldSchema) { _ in
EmptyContext()
}
let app = Application(router: router)
Expand All @@ -76,7 +76,7 @@ struct HTTPStatusCodeGraphQLJSONTests {
@Test func documentValidationFailureGivesBadRequest() async throws {
// https://github.com/graphql/graphql-over-http/blob/main/spec/GraphQLOverHTTP.md#document-validation-failure-1
let router = Router()
router.graphql(schema: helloWorldSchema) { _, _ in
router.graphql(schema: helloWorldSchema) { _ in
EmptyContext()
}
let app = Application(router: router)
Expand All @@ -97,7 +97,7 @@ struct HTTPStatusCodeGraphQLJSONTests {
@Test func operationCannotBeDeterminedGivesBadRequest() async throws {
// https://github.com/graphql/graphql-over-http/blob/main/spec/GraphQLOverHTTP.md#operation-cannot-be-determined-1
let router = Router()
router.graphql(schema: helloWorldSchema) { _, _ in
router.graphql(schema: helloWorldSchema) { _ in
EmptyContext()
}
let app = Application(router: router)
Expand Down Expand Up @@ -136,7 +136,7 @@ struct HTTPStatusCodeGraphQLJSONTests {
)
)
let router = Router()
router.graphql(schema: schema) { _, _ in
router.graphql(schema: schema) { _ in
EmptyContext()
}
let app = Application(router: router)
Expand Down Expand Up @@ -171,7 +171,7 @@ struct HTTPStatusCodeGraphQLJSONTests {
)
)
let router = Router()
router.graphql(schema: schema) { _, _ in
router.graphql(schema: schema) { _ in
EmptyContext()
}
let app = Application(router: router)
Expand Down
14 changes: 7 additions & 7 deletions Tests/GraphQLHummingbirdTests/HTTPStatusCodeJSONTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ struct HTTPStatusCodeJSONTests {
@Test func parsingFailureGivesBadRequest() async throws {
// https://github.com/graphql/graphql-over-http/blob/main/spec/GraphQLOverHTTP.md#json-parsing-failure
let router = Router()
router.graphql(schema: helloWorldSchema) { _, _ in
router.graphql(schema: helloWorldSchema) { _ in
EmptyContext()
}
let app = Application(router: router)
Expand All @@ -36,7 +36,7 @@ struct HTTPStatusCodeJSONTests {
@Test func invalidParametersGivesBadRequest() async throws {
// https://github.com/graphql/graphql-over-http/blob/main/spec/GraphQLOverHTTP.md#invalid-parameters
let router = Router()
router.graphql(schema: helloWorldSchema) { _, _ in
router.graphql(schema: helloWorldSchema) { _ in
EmptyContext()
}
let app = Application(router: router)
Expand All @@ -56,7 +56,7 @@ struct HTTPStatusCodeJSONTests {
@Test func documentValidationFailureGivesOk() async throws {
// https://github.com/graphql/graphql-over-http/blob/main/spec/GraphQLOverHTTP.md#document-validation-failure
let router = Router()
router.graphql(schema: helloWorldSchema) { _, _ in
router.graphql(schema: helloWorldSchema) { _ in
EmptyContext()
}
let app = Application(router: router)
Expand All @@ -77,7 +77,7 @@ struct HTTPStatusCodeJSONTests {
@Test func documentParsingFailureGivesOk() async throws {
// https://github.com/graphql/graphql-over-http/blob/main/spec/GraphQLOverHTTP.md#document-parsing-failure
let router = Router()
router.graphql(schema: helloWorldSchema) { _, _ in
router.graphql(schema: helloWorldSchema) { _ in
EmptyContext()
}
let app = Application(router: router)
Expand Down Expand Up @@ -116,7 +116,7 @@ struct HTTPStatusCodeJSONTests {
)
)
let router = Router()
router.graphql(schema: schema) { _, _ in
router.graphql(schema: schema) { _ in
EmptyContext()
}
let app = Application(router: router)
Expand All @@ -138,7 +138,7 @@ struct HTTPStatusCodeJSONTests {
@Test func operationCannotBeDeterminedGivesOk() async throws {
// https://github.com/graphql/graphql-over-http/blob/main/spec/GraphQLOverHTTP.md#operation-cannot-be-determined
let router = Router()
router.graphql(schema: helloWorldSchema) { _, _ in
router.graphql(schema: helloWorldSchema) { _ in
EmptyContext()
}
let app = Application(router: router)
Expand Down Expand Up @@ -171,7 +171,7 @@ struct HTTPStatusCodeJSONTests {
)
)
let router = Router()
router.graphql(schema: schema) { _, _ in
router.graphql(schema: schema) { _ in
EmptyContext()
}
let app = Application(router: router)
Expand Down
Loading