NestJS shared request context example

Note Statistics

Note Statistics

  • Viewed 2291 times
Sun, 03/28/2021 - 13:36

Here is short example of how to create a shared request or HTTP context in a NestJS service.

In this example

  • A middleware is used to set context data
  • A Context provider is used to prodive context data

Let's start by defining a provider for http context

// request-context.ts
import { createParamDecorator, HttpException, HttpStatus, Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
import httpContext from 'express-http-context';

@Injectable()
export class RequestContextProvider {
    get(key) {
        return httpContext.get(key)
    }

    set(key, value) {
        return httpContext.set(key, value)
    }
}

Next, Let's define a middleware

// request-context.ts

@Injectable()
export class RequestContextMiddleware implements NestMiddleware {
    constructor(private requestContextProvider: RequestContextProvider) { }

    use(req: Request, res: Response, next: NextFunction) {

        // first run express-http-context middleware
        httpContext.middleware(req, res, () => {
            // set context data
            // for example extract user data from JWT
            const [, token] = req.headers.authorization.split(' ')
            const decoded: Record<string, unknown> = jwt_decode(token)
            this.requestContextProvider.set('userId', decoded.userId)
            next();
        })
    }
}

In the application definition apply this middleware

//app.module.ts

import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { RequestContextMiddleware, RequestContextProvider } from './common/request-context';
import { MyModule } from './my/my.module';

@Module({
    imports: [MyModule],
    providers: [RequestContextProvider]
})
export class AppModule implements NestModule {
    configure(consumer: MiddlewareConsumer) {
        consumer
            .apply(RequestContextMiddleware)
            .forRoutes('*');
    }
}

Now let's use this provider in our controllers

//my.controller.ts

import { Controller, Post } from '@nestjs/common';

@Controller('my')
export class MyController {
    constructor(private requestContextProvider: RequestContextProvider) { }
    @Post()
    doSomething(): string {
        const userId = this.requestContextProvider.get('userId')
        //  do something with user ID
        //...
    }
}

Another option is to provide decorators for wanted context data, for example a user ID from the context

// request-context.ts

import { createParamDecorator, HttpException, HttpStatus } from '@nestjs/common'
import httpContext from 'express-http-context'

export const UserId = createParamDecorator(() => {
    const userId = httpContext.get('userId')
    if (!userId) {
        throw new HttpException(
            'Missing authorization credentials',
            HttpStatus.FORBIDDEN
        )
    }
    return userId;
})
Authored by
Tags