1
+ import { ApolloServer , type BaseContext } from '@apollo/server' ;
2
+ import { ApolloServerPluginLandingPageGraphQLPlayground } from '@apollo/server-plugin-landing-page-graphql-playground' ;
3
+ import { ApolloServerErrorCode } from '@apollo/server/errors' ;
4
+ import { expressMiddleware } from '@apollo/server/express4' ;
5
+ import { ApolloServerPluginLandingPageDisabled } from '@apollo/server/plugin/disabled' ;
6
+ import { ApolloServerPluginDrainHttpServer } from '@apollo/server/plugin/drainHttpServer' ;
1
7
import { HttpStatus } from '@nestjs/common' ;
2
8
import { loadPackage } from '@nestjs/common/utils/load-package.util' ;
3
9
import { isFunction } from '@nestjs/common/utils/shared.utils' ;
4
10
import { AbstractGraphQLDriver } from '@nestjs/graphql' ;
5
- import {
6
- ApolloError ,
7
- ApolloServerBase ,
8
- ApolloServerPluginLandingPageDisabled ,
9
- ApolloServerPluginLandingPageGraphQLPlayground ,
10
- AuthenticationError ,
11
- ForbiddenError ,
12
- PluginDefinition ,
13
- UserInputError ,
14
- } from 'apollo-server-core' ;
15
11
import { GraphQLError , GraphQLFormattedError } from 'graphql' ;
16
12
import * as omit from 'lodash.omit' ;
17
13
import { ApolloDriverConfig } from '../interfaces' ;
18
14
import { createAsyncIterator } from '../utils/async-iterator.util' ;
19
15
20
- const apolloPredefinedExceptions : Partial <
21
- Record < HttpStatus , typeof ApolloError | typeof UserInputError >
22
- > = {
23
- [ HttpStatus . BAD_REQUEST ] : UserInputError ,
24
- [ HttpStatus . UNAUTHORIZED ] : AuthenticationError ,
25
- [ HttpStatus . FORBIDDEN ] : ForbiddenError ,
16
+ const apolloPredefinedExceptions : Partial < Record < HttpStatus , string > > = {
17
+ [ HttpStatus . BAD_REQUEST ] : ApolloServerErrorCode . BAD_REQUEST ,
18
+ [ HttpStatus . UNAUTHORIZED ] : 'UNAUTHENTICATED' ,
19
+ [ HttpStatus . FORBIDDEN ] : 'FORBIDDEN' ,
26
20
} ;
27
21
28
22
export abstract class ApolloBaseDriver <
29
23
T extends Record < string , any > = ApolloDriverConfig ,
30
24
> extends AbstractGraphQLDriver < T > {
31
- protected _apolloServer : ApolloServerBase ;
25
+ protected apolloServer : ApolloServer < BaseContext > ;
32
26
33
- get instance ( ) : ApolloServerBase {
34
- return this . _apolloServer ;
27
+ get instance ( ) : ApolloServer < BaseContext > {
28
+ return this . apolloServer ;
35
29
}
36
30
37
31
public async start ( apolloOptions : T ) {
@@ -48,7 +42,7 @@ export abstract class ApolloBaseDriver<
48
42
}
49
43
50
44
public stop ( ) {
51
- return this . _apolloServer ?. stop ( ) ;
45
+ return this . apolloServer ?. stop ( ) ;
52
46
}
53
47
54
48
public async mergeDefaultOptions ( options : T ) : Promise < T > {
@@ -68,9 +62,7 @@ export abstract class ApolloBaseDriver<
68
62
defaults = {
69
63
...defaults ,
70
64
plugins : [
71
- ApolloServerPluginLandingPageGraphQLPlayground (
72
- playgroundOptions ,
73
- ) as PluginDefinition ,
65
+ ApolloServerPluginLandingPageGraphQLPlayground ( playgroundOptions ) ,
74
66
] ,
75
67
} ;
76
68
} else if (
@@ -80,7 +72,7 @@ export abstract class ApolloBaseDriver<
80
72
) {
81
73
defaults = {
82
74
...defaults ,
83
- plugins : [ ApolloServerPluginLandingPageDisabled ( ) as PluginDefinition ] ,
75
+ plugins : [ ApolloServerPluginLandingPageDisabled ( ) ] ,
84
76
} ;
85
77
}
86
78
@@ -116,67 +108,80 @@ export abstract class ApolloBaseDriver<
116
108
}
117
109
118
110
protected async registerExpress (
119
- apolloOptions : T ,
111
+ options : T ,
120
112
{ preStartHook } : { preStartHook ?: ( ) => void } = { } ,
121
113
) {
122
- const { ApolloServer } = loadPackage (
123
- 'apollo-server-express' ,
124
- 'GraphQLModule' ,
125
- ( ) => require ( 'apollo-server-express' ) ,
126
- ) ;
127
- const { disableHealthCheck, path, onHealthCheck, cors, bodyParserConfig } =
128
- apolloOptions ;
114
+ const { path, typeDefs, resolvers, schema } = options ;
129
115
130
116
const httpAdapter = this . httpAdapterHost . httpAdapter ;
131
117
const app = httpAdapter . getInstance ( ) ;
118
+ const drainHttpServerPlugin = ApolloServerPluginDrainHttpServer ( {
119
+ httpServer : httpAdapter . getHttpServer ( ) ,
120
+ } ) ;
132
121
133
122
preStartHook ?.( ) ;
134
123
135
- const apolloServer = new ApolloServer ( apolloOptions as any ) ;
136
- await apolloServer . start ( ) ;
124
+ const server = new ApolloServer ( {
125
+ typeDefs,
126
+ resolvers,
127
+ schema,
128
+ ...options ,
129
+ plugins : options . plugins
130
+ ? options . plugins . concat ( [ drainHttpServerPlugin ] )
131
+ : [ drainHttpServerPlugin ] ,
132
+ } ) ;
133
+
134
+ await server . start ( ) ;
137
135
138
- apolloServer . applyMiddleware ( {
139
- app,
136
+ app . use (
140
137
path ,
141
- disableHealthCheck,
142
- onHealthCheck,
143
- cors,
144
- bodyParserConfig,
145
- } ) ;
138
+ expressMiddleware ( server , {
139
+ context : options . context ,
140
+ } ) ,
141
+ ) ;
146
142
147
- this . _apolloServer = apolloServer ;
143
+ this . apolloServer = server ;
148
144
}
149
145
150
146
protected async registerFastify (
151
- apolloOptions : T ,
147
+ options : T ,
152
148
{ preStartHook } : { preStartHook ?: ( ) => void } = { } ,
153
149
) {
154
- const { ApolloServer } = loadPackage (
155
- 'apollo-server- fastify' ,
150
+ const { fastifyApolloDrainPlugin , fastifyApolloHandler } = loadPackage (
151
+ '@as-integrations/ fastify' ,
156
152
'GraphQLModule' ,
157
- ( ) => require ( 'apollo-server- fastify' ) ,
153
+ ( ) => require ( '@as-integrations/ fastify' ) ,
158
154
) ;
159
155
160
156
const httpAdapter = this . httpAdapterHost . httpAdapter ;
161
157
const app = httpAdapter . getInstance ( ) ;
162
158
159
+ const { path, typeDefs, resolvers, schema } = options ;
160
+ const apolloDrainPlugin = fastifyApolloDrainPlugin ( app ) ;
161
+
163
162
preStartHook ?.( ) ;
164
- const apolloServer = new ApolloServer ( apolloOptions as any ) ;
165
- await apolloServer . start ( ) ;
166
-
167
- const { disableHealthCheck, onHealthCheck, cors, bodyParserConfig, path } =
168
- apolloOptions ;
169
- await app . register (
170
- apolloServer . createHandler ( {
171
- disableHealthCheck,
172
- onHealthCheck,
173
- cors,
174
- bodyParserConfig,
175
- path,
163
+
164
+ const server = new ApolloServer < BaseContext > ( {
165
+ typeDefs,
166
+ resolvers,
167
+ schema,
168
+ ...options ,
169
+ plugins : options . plugins
170
+ ? options . plugins . concat ( [ apolloDrainPlugin ] )
171
+ : [ apolloDrainPlugin ] ,
172
+ } ) ;
173
+
174
+ await server . start ( ) ;
175
+
176
+ app . route ( {
177
+ url : path ,
178
+ method : [ 'GET' , 'POST' , 'OPTIONS' ] ,
179
+ handler : fastifyApolloHandler ( server , {
180
+ context : options . context ,
176
181
} ) ,
177
- ) ;
182
+ } ) ;
178
183
179
- this . _apolloServer = apolloServer ;
184
+ this . apolloServer = server ;
180
185
}
181
186
182
187
private wrapFormatErrorFn ( options : T ) {
@@ -201,19 +206,25 @@ export abstract class ApolloBaseDriver<
201
206
const exceptionRef = originalError ?. extensions ?. exception ;
202
207
const isHttpException =
203
208
exceptionRef ?. response ?. statusCode && exceptionRef ?. status ;
204
-
205
209
if ( ! isHttpException ) {
206
210
return originalError as GraphQLFormattedError ;
207
211
}
208
- let error : ApolloError ;
212
+ let error : GraphQLError ;
209
213
210
214
const httpStatus = exceptionRef ?. status ;
211
215
if ( httpStatus in apolloPredefinedExceptions ) {
212
- error = new apolloPredefinedExceptions [ httpStatus ] (
213
- exceptionRef ?. message ,
214
- ) ;
216
+ error = new GraphQLError ( exceptionRef ?. message , {
217
+ extensions : {
218
+ code : apolloPredefinedExceptions [ httpStatus ] ,
219
+ } ,
220
+ } ) ;
215
221
} else {
216
- error = new ApolloError ( exceptionRef . message , httpStatus ?. toString ( ) ) ;
222
+ error = new GraphQLError ( exceptionRef . message , {
223
+ extensions : {
224
+ code : ApolloServerErrorCode . INTERNAL_SERVER_ERROR ,
225
+ status : httpStatus ,
226
+ } ,
227
+ } ) ;
217
228
}
218
229
219
230
error . stack = exceptionRef ?. stacktrace ;
@@ -227,18 +238,26 @@ export abstract class ApolloBaseDriver<
227
238
originalOptions : ApolloDriverConfig = { ...targetOptions } ,
228
239
) {
229
240
if ( ! targetOptions . context ) {
230
- targetOptions . context = ( { req, request } ) => ( { req : req ?? request } ) ;
241
+ targetOptions . context = async ( contextOrRequest ) => {
242
+ return {
243
+ // New ApolloServer fastify integration has Request as first parameter to the Context function
244
+ req : contextOrRequest . req ?? contextOrRequest ,
245
+ } ;
246
+ } ;
231
247
} else if ( isFunction ( targetOptions . context ) ) {
232
248
targetOptions . context = async ( ...args : unknown [ ] ) => {
233
249
const ctx = await ( originalOptions . context as Function ) ( ...args ) ;
234
- const { req, request } = args [ 0 ] as Record < string , unknown > ;
235
- return this . assignReqProperty ( ctx , req ?? request ) ;
250
+ const contextOrRequest = args [ 0 ] as Record < string , unknown > ;
251
+ return this . assignReqProperty (
252
+ ctx ,
253
+ contextOrRequest . req ?? contextOrRequest ,
254
+ ) ;
236
255
} ;
237
256
} else {
238
- targetOptions . context = ( { req , request } : Record < string , unknown > ) => {
257
+ targetOptions . context = async ( contextOrRequest ) => {
239
258
return this . assignReqProperty (
240
259
originalOptions . context as Record < string , any > ,
241
- req ?? request ,
260
+ contextOrRequest . req ?? contextOrRequest ,
242
261
) ;
243
262
} ;
244
263
}
0 commit comments