1
- import {
2
- ApolloServerPlugin as ApolloPlugin ,
3
- GraphQLRequestContext as RequestContext ,
4
- GraphQLRequestListener as RequestListener ,
5
- } from '@apollo/server' ;
6
- import { Plugin } from '@nestjs/apollo' ;
7
- import {
8
- CallHandler ,
9
- ExecutionContext ,
10
- Injectable ,
11
- NestInterceptor ,
12
- OnModuleDestroy ,
13
- } from '@nestjs/common' ;
1
+ import { Injectable , OnModuleDestroy } from '@nestjs/common' ;
14
2
import { AsyncLocalStorage } from 'async_hooks' ;
15
3
import { GqlContextType as ContextType } from '~/common' ;
16
- import { HttpMiddleware as NestMiddleware } from '~/core/http' ;
17
4
import { AsyncLocalStorageNoContextException } from '../async-local-storage-no-context.exception' ;
5
+ import { Plugin } from './plugin.decorator' ;
18
6
19
7
export const isGqlContext = ( object : unknown ) : object is ContextType =>
20
8
object != null && typeof object === 'object' && isGqlContext . KEY in object ;
@@ -30,85 +18,29 @@ export abstract class GqlContextHost {
30
18
readonly context : ContextType ;
31
19
}
32
20
33
- /**
34
- * This is necessary to allow global pipes to have access to GraphQL request context.
35
- * At least until this is resolved: https://github.com/nestjs/graphql/issues/325
36
- */
37
21
@Injectable ( )
38
22
@Plugin ( )
39
- export class GqlContextHostImpl
40
- implements
41
- GqlContextHost ,
42
- NestMiddleware ,
43
- NestInterceptor ,
44
- OnModuleDestroy ,
45
- ApolloPlugin < ContextType >
46
- {
47
- als = new AsyncLocalStorage < {
48
- ctx ?: ContextType ;
49
- execution ?: ExecutionContext ;
50
- } > ( ) ;
23
+ export class GqlContextHostImpl implements GqlContextHost , OnModuleDestroy {
24
+ als = new AsyncLocalStorage < ContextType > ( ) ;
51
25
52
- /**
53
- * Unwrap the ALS store or throw error if called incorrectly.
54
- */
55
26
get context ( ) {
56
- const store = this . als . getStore ( ) ;
57
- if ( store ?. ctx ) {
58
- return store . ctx ;
59
- }
60
-
61
- const message = 'The GraphQL context is not available yet.' ;
62
- if ( ! store ) {
63
- throw new AsyncLocalStorageNoContextException ( message ) ;
64
- }
65
- if (
66
- ! store . ctx &&
67
- store . execution &&
68
- store . execution . getType ( ) !== 'graphql'
69
- ) {
70
- throw new NotGraphQLContext ( message ) ;
27
+ const context = this . als . getStore ( ) ;
28
+ if ( context ) {
29
+ return context ;
71
30
}
72
- throw new Error ( message ) ;
31
+ throw new AsyncLocalStorageNoContextException (
32
+ 'The GraphQL context is not available yet.' ,
33
+ ) ;
73
34
}
74
35
75
- use : NestMiddleware [ 'use' ] = ( req , res , next ) => {
76
- // Connect middleware is the only place we get a function where we can
77
- // completely wrap the request for the use of an ALS context.
78
- this . attachScope ( next ) ;
36
+ onExecute : Plugin [ 'onExecute' ] = ( { executeFn, setExecuteFn, args } ) => {
37
+ const ctx = args . contextValue ;
38
+ setExecuteFn ( ( ...args ) => {
39
+ return this . als . run ( ctx , executeFn , ...args ) ;
40
+ } ) ;
79
41
} ;
80
42
81
- attachScope < R > ( fn : ( ) => R ) : R {
82
- // Just give it a placeholder object for now which we populate below.
83
- return this . als . run ( { } , fn ) ;
84
- }
85
-
86
- intercept ( context : ExecutionContext , next : CallHandler ) {
87
- const store = this . als . getStore ( ) ;
88
- if ( store ) {
89
- store . execution = context ;
90
- }
91
-
92
- return next . handle ( ) ;
93
- }
94
-
95
- /**
96
- * Attach GQL context to the ALS store now that we have it.
97
- */
98
- async requestDidStart ( {
99
- contextValue : context ,
100
- } : RequestContext < ContextType > ) : Promise < RequestListener < ContextType > > {
101
- const store = this . als . getStore ( ) ;
102
- if ( ! store ) {
103
- return { } ;
104
- }
105
- store . ctx = context ;
106
- return { } ;
107
- }
108
-
109
43
onModuleDestroy ( ) {
110
44
this . als . disable ( ) ;
111
45
}
112
46
}
113
-
114
- export class NotGraphQLContext extends Error { }
0 commit comments