Abhishek Chaniyara
Abhishek Chaniyara

Reputation: 300

Unable to get request body via NestJS Middleware having application with FastifyAdapter

I am working with the NestJS project, and want to log each and every request. I am using Fastify in my REST API. I made a nest middleware for getting request body:

import { HttpException, Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';

@Injectable()
export class LoggerMiddleware implements NestMiddleware {
        use(req: Request, res: Response, next: NextFunction) {
                const { httpVersion, headers, method, baseUrl, params, query, body } = req;
                // body is Undefined!
                console.log('Request Body...', body);                
                next();
        }
}

When I print a log into the console, I got the output undefined.

app.module.ts:

export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(LoggerMiddleware)
      .forRoutes('*');
  }
}

main.ts:

import { NestFactory } from '@nestjs/core';
import {
  FastifyAdapter,
  NestFastifyApplication,
} from '@nestjs/platform-fastify';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create<NestFastifyApplication>(
    AppModule,
    new FastifyAdapter()
  );
  await app.listen(3000);
}
bootstrap()

Upvotes: 1

Views: 3629

Answers (1)

Or207
Or207

Reputation: 111

Fastify and Express treats Middlewares differently

When using Fastify with NestJS, Fastify use a package called middie to support middleware like Express, but this compatibility doesn't extend fully to things like req.body (BTW - same goes to req.params and req.query) within middleware in the same way as Express does.

The Core Issue

When using Fastify under the hood in NestJS, these properties are not automatically populated in middleware because Fastify processes route parameters and query parameters after the middleware is executed. This is why you're seeing req.body as undefined in the middleware.

To work around this, there are a couple of approaches you can take depending on what you're trying to accomplish.

Solution 1 - Use onRequest Hook to Attach the Missing Data on req.raw

One way to resolve the issue of not having access to req.params, req.query, or req.body in NestJS middleware when using Fastify is to use Fastify's onRequest hook. This hook is executed early in the lifecycle, and we can use it to attach params, query, and body to req.raw, which is the original request object.

This way, you can maintain compatibility with middleware that expects these properties (like in Express).

Step-by-Step Guide: Create a Utility Function to Attach Data to req.raw:

// utils/attach-request-data.ts
import { FastifyRequest, FastifyReply } from 'fastify';

/**
 * Attaches params, query, and body from Fastify's request object to the raw request object.
 * This ensures compatibility with middleware that expects these properties to be available.
 */
export function attachRequestDataToRaw(req: FastifyRequest, reply: FastifyReply, done: () => void) {
  req.raw["params"]= req.params;
  req.raw["query"]= req.query;
  req.raw["body"]= req.body;

  done();
}

Use the Utility Function in main.ts: In your main.ts, register the hook to attach the required data:

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { FastifyInstance } from 'fastify';
import { attachRequestDataToRaw } from './utils/attach-request-data';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  // Get the Fastify instance from Nest's HTTP adapter
  const fastifyInstance: FastifyInstance = app.getHttpAdapter().getInstance();

  // Use the attachRequestDataToRaw function to attach params, query, and body
  fastifyInstance.addHook('onRequest', attachRequestDataToRaw);

  await app.listen(3000);
}
bootstrap();

Now - you can access req.raw.params, req.raw.query, and req.raw.body in any middleware by req.params, req.query and req.body :-)

Why This Solution Works

Middleware in Fastify runs early in the request lifecycle, before Fastify has matched the route and populated the params, query, and body fields. By using the onRequest hook, we can manually copy the params, query, and body fields from Fastify's request object to req.raw, which is accessible throughout the rest of the request lifecycle, including in middleware.


Solution 2 - Use Interceptors or Guards Instead which have access to these properties after the route is matched

Example: Using an Interceptor Interceptors in NestJS are applied after the routing process, so req.params, req.query, and req.body are accessible.

import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
import { FastifyRequest } from 'fastify';

@Injectable()
export class ParamLoggingInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    const request = context.switchToHttp().getRequest<FastifyRequest>();

    // Now you can access request parameters, query, and body
    console.log('Request Params:', request.params);
    console.log('Query Params:', request.query);
    console.log('Body:', request.body);

    return next.handle();
  }
}

ref

  1. https://fastify.dev/docs/latest/Reference/Middleware/
  2. https://github.com/fastify/middie
  3. https://docs.nestjs.com/interceptors

Upvotes: 1

Related Questions