-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathRouter+graphql.swift
More file actions
141 lines (133 loc) · 6.76 KB
/
Copy pathRouter+graphql.swift
File metadata and controls
141 lines (133 loc) · 6.76 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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
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<Context, EmptyWebSocketInit, Void> = .init(),
computeContext: @Sendable @escaping (GraphQLContextComputationInputs<Context, Void>) async throws -> GraphQLContext
) -> Self {
// https://github.com/graphql/graphql-over-http/blob/main/spec/GraphQLOverHTTP.md#request
let handler = GraphQLHandler<Context, GraphQLContext, EmptyWebSocketInit, Void>(
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,
WebSocketInitResult: Sendable
>(
_ path: RouterPath = "graphql",
schema: GraphQLSchema,
rootValue: any Sendable = (),
config: GraphQLConfig<Context, WebSocketInit, WebSocketInitResult>,
computeContext: @Sendable @escaping (GraphQLContextComputationInputs<Context, WebSocketInitResult>) async throws -> GraphQLContext
) -> Self {
let handler = GraphQLHandler<Context, GraphQLContext, WebSocketInit, WebSocketInitResult>(
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
}
/// 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.
/// - computeContext: A closure used to compute the GraphQL context from incoming requests. This must be provided.
@discardableResult
func graphqlWebSocket<GraphQLContext: Sendable>(
_ path: RouterPath = "graphql",
schema: GraphQLSchema,
rootValue: any Sendable = (),
computeContext: @Sendable @escaping (GraphQLContextComputationInputs<Context, Void>) async throws -> GraphQLContext
) -> Self {
// This is just an overload that allows not passing `config`, since we cannot use a default argument that
// uses `Context` without getting `error: generic parameter 'Self' could not be inferred` compilation errors
graphqlWebSocket(
path,
schema: schema,
rootValue: rootValue,
config: GraphQLConfig<Context, EmptyWebSocketInit, Void>(),
computeContext: computeContext
)
}
}