Skip to content

Commit 23a4f40

Browse files
committed
Convert ALS for gql context to yoga plugin
1 parent 1d16b94 commit 23a4f40

File tree

3 files changed

+18
-104
lines changed

3 files changed

+18
-104
lines changed
Lines changed: 15 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,8 @@
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';
142
import { AsyncLocalStorage } from 'async_hooks';
153
import { GqlContextType as ContextType } from '~/common';
16-
import { HttpMiddleware as NestMiddleware } from '~/core/http';
174
import { AsyncLocalStorageNoContextException } from '../async-local-storage-no-context.exception';
5+
import { Plugin } from './plugin.decorator';
186

197
export const isGqlContext = (object: unknown): object is ContextType =>
208
object != null && typeof object === 'object' && isGqlContext.KEY in object;
@@ -30,85 +18,29 @@ export abstract class GqlContextHost {
3018
readonly context: ContextType;
3119
}
3220

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-
*/
3721
@Injectable()
3822
@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>();
5125

52-
/**
53-
* Unwrap the ALS store or throw error if called incorrectly.
54-
*/
5526
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;
7130
}
72-
throw new Error(message);
31+
throw new AsyncLocalStorageNoContextException(
32+
'The GraphQL context is not available yet.',
33+
);
7334
}
7435

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+
});
7941
};
8042

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-
10943
onModuleDestroy() {
11044
this.als.disable();
11145
}
11246
}
113-
114-
export class NotGraphQLContext extends Error {}

src/core/graphql/graphql.module.ts

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
1-
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
2-
import { APP_INTERCEPTOR } from '@nestjs/core';
1+
import { Module } from '@nestjs/common';
32
import { GraphQLModule as NestGraphqlModule } from '@nestjs/graphql';
4-
import { HttpAdapterHost } from '~/core/http';
53
import { TracingModule } from '../tracing';
64
import { Driver } from './driver';
75
import { GqlContextHost, GqlContextHostImpl } from './gql-context.host';
@@ -37,19 +35,7 @@ export class GraphqlOptionsModule {}
3735
providers: [
3836
GqlContextHostImpl,
3937
{ provide: GqlContextHost, useExisting: GqlContextHostImpl },
40-
{ provide: APP_INTERCEPTOR, useExisting: GqlContextHostImpl },
4138
],
4239
exports: [NestGraphqlModule, GqlContextHost],
4340
})
44-
export class GraphqlModule implements NestModule {
45-
constructor(
46-
private readonly middleware: GqlContextHostImpl,
47-
private readonly app: HttpAdapterHost,
48-
) {}
49-
50-
configure(consumer: MiddlewareConsumer) {
51-
// Always attach our GQL Context middleware.
52-
// It has its own logic to handle non-gql requests.
53-
consumer.apply(this.middleware.use).forRoutes('*');
54-
}
55-
}
41+
export class GraphqlModule {}

src/core/graphql/index.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,3 @@
11
export * from './graphql.module';
2-
export {
3-
GqlContextHost,
4-
isGqlContext,
5-
NotGraphQLContext,
6-
} from './gql-context.host';
2+
export { GqlContextHost, isGqlContext } from './gql-context.host';
73
export * from './plugin.decorator';

0 commit comments

Comments
 (0)