-
Notifications
You must be signed in to change notification settings - Fork 2k
/
ApolloServer.ts
168 lines (149 loc) · 4.57 KB
/
ApolloServer.ts
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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
import { ApolloServerBase, GraphQLOptions } from 'apollo-server-core';
import { processRequest as processFileUploads } from '@apollographql/apollo-upload-server';
import { ServerResponse } from 'http';
import { send } from 'micro';
import { renderPlaygroundPage } from '@apollographql/graphql-playground-html';
import { parseAll } from 'accept';
import { graphqlMicro } from './microApollo';
import { MicroRequest } from './types';
export interface ServerRegistration {
path?: string;
disableHealthCheck?: boolean;
onHealthCheck?: (req: MicroRequest) => Promise<any>;
}
export class ApolloServer extends ApolloServerBase {
// Extract Apollo Server options from the request.
async createGraphQLServerOptions(
req: MicroRequest,
res: ServerResponse,
): Promise<GraphQLOptions> {
return super.graphQLServerOptions({ req, res });
}
// Prepares and returns an async function that can be used by Micro to handle
// GraphQL requests.
public createHandler({
path,
disableHealthCheck,
onHealthCheck,
}: ServerRegistration = {}) {
return async (req, res) => {
this.graphqlPath = path || '/graphql';
await this.handleFileUploads(req);
(await this.handleHealthCheck({
req,
res,
disableHealthCheck,
onHealthCheck,
})) ||
this.handleGraphqlRequestsWithPlayground({ req, res }) ||
(await this.handleGraphqlRequestsWithServer({ req, res })) ||
send(res, 404, null);
};
}
// This integration supports file uploads.
protected supportsUploads(): boolean {
return true;
}
// This integration supports subscriptions.
protected supportsSubscriptions(): boolean {
return true;
}
// If health checking is enabled, trigger the `onHealthCheck`
// function when the health check URL is requested.
private async handleHealthCheck({
req,
res,
disableHealthCheck,
onHealthCheck,
}: {
req: MicroRequest;
res: ServerResponse;
disableHealthCheck?: boolean;
onHealthCheck?: (req: MicroRequest) => Promise<any>;
}): Promise<boolean> {
let handled = false;
if (
!disableHealthCheck &&
req.url === '/.well-known/apollo/server-health'
) {
// Response follows
// https://tools.ietf.org/html/draft-inadarei-api-health-check-01
res.setHeader('Content-Type', 'application/health+json');
if (onHealthCheck) {
try {
await onHealthCheck(req);
} catch (error) {
send(res, 503, { status: 'fail' });
handled = true;
}
}
if (!handled) {
send(res, 200, { status: 'pass' });
handled = true;
}
}
return handled;
}
// If the `playgroundOptions` are set, register a `graphql-playground` instance
// (not available in production) that is then used to handle all
// incoming GraphQL requests.
private handleGraphqlRequestsWithPlayground({
req,
res,
}: {
req: MicroRequest;
res: ServerResponse;
}): boolean {
let handled = false;
if (this.playgroundOptions && req.method === 'GET') {
const accept = parseAll(req.headers);
const types = accept.mediaTypes as string[];
const prefersHTML =
types.find(
(x: string) => x === 'text/html' || x === 'application/json',
) === 'text/html';
if (prefersHTML) {
const middlewareOptions = {
endpoint: this.graphqlPath,
subscriptionEndpoint: this.subscriptionsPath,
...this.playgroundOptions,
};
send(res, 200, renderPlaygroundPage(middlewareOptions));
handled = true;
}
}
return handled;
}
// Handle incoming GraphQL requests using Apollo Server.
private async handleGraphqlRequestsWithServer({
req,
res,
}: {
req: MicroRequest;
res: ServerResponse;
}): Promise<boolean> {
let handled = false;
const url = req.url.split('?')[0];
if (url === this.graphqlPath) {
const graphqlHandler = graphqlMicro(
this.createGraphQLServerOptions.bind(this),
);
const responseData = await graphqlHandler(req, res);
send(res, 200, responseData);
handled = true;
}
return handled;
}
// If file uploads are detected, prepare them for easier handling with
// the help of `apollo-upload-server`.
private async handleFileUploads(req: MicroRequest) {
const contentType = req.headers['content-type'];
if (
this.uploadsConfig &&
contentType &&
contentType.startsWith('multipart/form-data')
) {
req.filePayload = await processFileUploads(req, this.uploadsConfig);
}
}
}