Skip to content

fix: FastifyAdapter bypasses global ValidationPipe for tz, resulting in 400 whitelistValidation error in production builds #2621

Closed as not planned
@puffyy

Description

@puffyy

Description

I’m experiencing an issue where my global ValidationPipe is registered but doesn’t run on requests when using the Fastify adapter in a production build (both locally and on my production server). Even though I see the registration logs, extra query parameters are neither stripped nor rejected.

Reproduction Steps:

  1. Setup Nest with FastifyAdapter
    `// main.ts
    import 'reflect-metadata';
    import { NestFactory } from '@nestjs/core';
    import { FastifyAdapter, NestFastifyApplication } from '@nestjs/platform-fastify';
    import { ValidationPipe, Logger } from '@nestjs/common';
    import { AppModule } from './app.module';

async function bootstrap() {
const app = await NestFactory.create(
AppModule,
new FastifyAdapter({ logger: true }),
);
const logger = new Logger('Bootstrap');
logger.log('FastifyAdapter registered');

app.useGlobalPipes(new ValidationPipe({
whitelist: true,
forbidNonWhitelisted: true,
transform: true,
transformOptions: { enableImplicitConversion: true },
}));
logger.log('ValidationPipe registered');

await app.listen(3000, '0.0.0.0');
logger.log('App is running');
}
bootstrap();`

  1. Define a simple DTO and controller
    `// dto/simple.dto.ts
    import { IsOptional, IsString } from 'class-validator';

export class SimpleDto {
@IsOptional()
@IsString()
tz?: string;
}

// controllers/simple.controller.ts
import { Controller, Get, Query } from '@nestjs/common';
import { SimpleDto } from '../dto/simple.dto';

@controller('simple')
export class SimpleController {
@get()
test(@query() dto: SimpleDto) {
return dto;
}
}`

  1. Build & run in production mode (locally)
    npm run build # uses tsconfig.build.json
    NODE_ENV=production node dist/main.js

  2. Send request with extra query param
    curl -i 'http://localhost:3000/simple?tz=Europe/Istanbul&foo=bar'

  3. Observe behavior
    Expected: Request is rejected with 400 Bad Request due to foo being non-whitelisted.
    Actual: Request succeeds (200 OK), returning both tz and foo intact.

Environment:

  • NestJS: 10.x
  • @nestjs/platform-fastify: 10.x
  • class-validator: 0.14.x
  • class-transformer: 0.5.x
  • Node.js: 20.x
  • tsconfig.build.json:

{ "extends": "./tsconfig.json", "compilerOptions": { "emitDecoratorMetadata": true, "experimentalDecorators": true, "target": "ES2021", "module": "CommonJS" }, "exclude": ["node_modules", "test", "dist"] }

Error Message:
`HTTP/1.1 400 Bad Request
Content-Type: application/json; charset=utf-8

{
"message": [
{
"property": "tz",
"constraints": {
"whitelistValidation": "property tz should not exist"
}
}
],
"error": "Bad Request",
"statusCode": 400
}`

Additional Context:

  • The same setup works correctly in development mode (NODE_ENV=development) and locally when running the production build (NODE_ENV=production).
  • I see both FastifyAdapter registered and ValidationPipe registered in logs in all environments.
  • Even with global settings whitelist: false and forbidNonWhitelisted: false, I still receive the whitelistValidation error for tz.
  • Using @Allow() on specific DTO properties also does not resolve the inconsistency.
  • Switching to Express adapter makes validation work as expected.
  • This issue occurs regardless of environment, but only when using the Fastify adapter in a production build.
  • Any guidance on fixing global pipes under Fastify in a production build would be greatly appreciated!

Metadata

Metadata

Assignees

No one assigned

    Labels

    status: needs triageIssues which needs to be reproduced to be verified report.type: fixIssues describing a broken feature.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions