Extend the WunderGraph server context

Using the context factory

When calling handler functions, be it a TypeScript operation or a hook, WunderGraph provides a context object which allows you to read information like the currently logged in user or the client request. However, in some circumstances you might also want you store additional data in that context.

To solve this problem, WunderGraph provides support for instantiating a custom context on every handler invocation. To use it, declare a function that returns your context type and pass it to createWunderGraphServer():

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
// wundergraph.server.ts
import { configureWunderGraphServer } from '@wundergraph/sdk/server';
export class MyContext {
cleanup() {
console.log('cleaning up');
}
hello() {
return 'world';
}
greet() {
console.log(`say hello ${this.hello()}`);
}
}
declare module '@wundergraph/sdk/server' {
export interface CustomContext {
request: MyContext;
}
}
export default configureWunderGraphServer(() => ({
hooks: {
queries: {
Countries: {
preResolve: async ({ operations, context }) => {
// Use your context from a hook
context.greet();
},
},
},
mutations: {},
},
context: {
request: {
create: async () => {
return new MyContext();
},
release: async (ctx) => {
ctx.cleanup();
},
},
},
}));

This will create a per-request context for every request, pass it to every handler and finally call context.release, giving it a chance to free any pending resources.

Additionally, this context can also be used from TypeScript functions:

1
2
3
4
5
6
7
8
9
import { createOperation } from '../../generated/wundergraph.factory';
export default createOperation.query({
handler: async ({ context }) => {
return {
hello: context.hello(), // Use your context from an operation
};
},
});

As well as from webhooks:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import type { WebhookHttpEvent, WebhookHttpResponse } from '@wundergraph/sdk/server';
import { createWebhook } from '../generated/wundergraph.webhooks';
export default createWebhook<WebhookHttpEvent, WebhookHttpResponse>({
handler: async (event, context) => {
return {
statusCode: 200,
headers: {
myResponseHeaderVar: 'test',
},
body: {
myResponseBodyVar: `hello ${context.context.hello()}`,
},
};
},
});

And embedded GraphQL servers:

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
export default configureWunderGraphServer(() => ({
/* ... */
context: {
request: {
create: async () => {
return new MyCustomContext();
},
},
},
graphqlServers: [
{
apiNamespace: 'embedded',
serverName: 'embedded',
schema: new GraphQLSchema({
query: new GraphQLObjectType<any, GraphQLExecutionContext>({
name: 'Query',
fields: {
fromCustomContext: {
type: GraphQLString,
resolve: async (parent, args, ctx) => {
return ctx.wundergraph.context.hello();
},
},
},
}),
}),
},
],
}));

Storing global data

Besides the per-request context, WunderGraph suports a global context too. This context gets instantiated once when the server starts and can be used when instantiating the per-request contexts:

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
// wundergraph.server.ts
class MyGlobalContext {
}
class MyRequestContext {
constructor(private ctx: MyGlobalContext)
}
declare module '@wundergraph/sdk/server' {
export interface CustomContext {
global: MyGlobalContext;
request: MyContext;
}
}
export default configureWunderGraphServer(() => ({
hooks: {
queries: {},
mutations: {},
},
context: {
global: {
create: async () => {
return new MyGlobalContext();
},
release: async (ctx/*: MyGlobalContext*/) => {
// Called at server shutdown
},
},
request: {
create: async (ctx/*: MyGlobalContext*/) => {
return new MyRequestContext(ctx);
},
release: async (ctx/*: MyRequestContext*/) => {
// Called after every response
},
},
},
});

This pattern can be used with popular dependency injection frameworks like awilix .

Was this article helpful to you?
Provide feedback

Edit this page