-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathRouter+graphql.swift
More file actions
110 lines (103 loc) · 5.09 KB
/
Copy pathRouter+graphql.swift
File metadata and controls
110 lines (103 loc) · 5.09 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
import GraphQL
import Hummingbird
import HummingbirdWebSocket
public extension RouterMethods {
/// Registers graphql routes that respond using the provided schema.
///
/// The resulting routes adhere to the [GraphQL over HTTP spec](https://github.com/graphql/graphql-over-http/blob/main/spec/GraphQLOverHTTP.md).
/// The configured IDE is available by making a `GET` request to the path with no query parameter.
///
/// - Parameters:
/// - path: The route that should respond to GraphQL requests. Both `GET` and `POST` routes are registered.
/// - schema: The GraphQL schema that should be used to respond to requests.
/// - rootValue: The `rootValue` GraphQL execution arg. This is the object passed to the root resolvers.
/// - config: GraphQL Handler configuration options. See type documentation for details.
/// - computeContext: A closure used to compute the GraphQL context from incoming requests. This must be provided.
@discardableResult
func graphql<GraphQLContext: Sendable>(
_ path: RouterPath = "graphql",
schema: GraphQLSchema,
rootValue: any Sendable = (),
config: GraphQLConfig<EmptyWebSocketInit> = .init(),
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>(
schema: schema,
rootValue: rootValue,
config: config,
computeContext: computeContext
)
get(path) { request, context -> Response in
// Get requests without a `query` parameter are considered to be IDE requests
let hasQueryParam = request.uri.query?.contains("query") ?? false
if !hasQueryParam {
switch config.ide.type {
case .graphiql:
let url = request.uri.path
// Since we cannot know if websockets has been registered, assume we have a websocket at the same route.
let subscriptionUrl = url.replacingOccurrences(of: "http://", with: "ws://").replacingOccurrences(of: "https://", with: "wss://")
return try await GraphiQLHandler.respond(
url: url,
subscriptionUrl: subscriptionUrl
)
case .none:
// Let this get caught by the graphQLRequest decoding
break
}
}
// Normal GET request handling
return try await handler.handleGet(request: request, context: context)
}
post(path) { request, context in
try await handler.handlePost(request: request, context: context)
}
return self
}
}
public extension RouterMethods where Context: WebSocketRequestContext {
/// Registers a graphql websocket route that responds using the provided schema.
///
/// WebSocket requests support the
/// [`graphql-transport-ws`](https://github.com/enisdenjo/graphql-ws/blob/master/PROTOCOL.md)
/// and [`graphql-ws`](https://github.com/apollographql/subscriptions-transport-ws/blob/master/PROTOCOL.md)
/// subprotocols.
///
/// - Parameters:
/// - path: The route that should respond to GraphQL requests. Both `GET` and `POST` routes are registered.
/// - schema: The GraphQL schema that should be used to respond to requests.
/// - rootValue: The `rootValue` GraphQL execution arg. This is the object passed to the root resolvers.
/// - config: GraphQL Handler configuration options. See type documentation for details. Note that all non-WebSocket values are ignored.
/// - computeContext: A closure used to compute the GraphQL context from incoming requests. This must be provided.
@discardableResult
func graphqlWebSocket<
GraphQLContext: Sendable,
WebSocketInit: Equatable & Codable & Sendable
>(
_ path: RouterPath = "graphql",
schema: GraphQLSchema,
rootValue: any Sendable = (),
config: GraphQLConfig<WebSocketInit> = GraphQLConfig<EmptyWebSocketInit>(),
computeContext: @Sendable @escaping (GraphQLContextComputationInputs<Context>) async throws -> GraphQLContext
) -> Self {
let handler = GraphQLHandler<Context, GraphQLContext, WebSocketInit>(
schema: schema,
rootValue: rootValue,
config: config,
computeContext: computeContext
)
ws(path, shouldUpgrade: { request, _ in
try handler.shouldUpgrade(request: request)
}) { inbound, outbound, context in
let subProtocol = try handler.negotiateSubProtocol(request: context.request)
try await handler.handleWebSocket(
inbound: inbound,
outbound: outbound,
context: context,
subProtocol: subProtocol,
logger: context.logger
)
}
return self
}
}