nestjs-patterns

NestJS architecture patterns for modules, controllers, providers, DTO validation, guards, interceptors, config, and production-grade TypeScript backends.

INSTALLATION
npx skills add https://github.com/affaan-m/everything-claude-code --skill nestjs-patterns
Run in your project or agent environment. Adjust flags if your CLI version differs.

SKILL.md

NestJS Development Patterns

Production-grade NestJS patterns for modular TypeScript backends.

When to Activate

  • Building NestJS APIs or services
  • Structuring modules, controllers, and providers
  • Adding DTO validation, guards, interceptors, or exception filters
  • Configuring environment-aware settings and database integrations
  • Testing NestJS units or HTTP endpoints

Project Structure

src/

├── app.module.ts

├── main.ts

├── common/

│   ├── filters/

│   ├── guards/

│   ├── interceptors/

│   └── pipes/

├── config/

│   ├── configuration.ts

│   └── validation.ts

├── modules/

│   ├── auth/

│   │   ├── auth.controller.ts

│   │   ├── auth.module.ts

│   │   ├── auth.service.ts

│   │   ├── dto/

│   │   ├── guards/

│   │   └── strategies/

│   └── users/

│       ├── dto/

│       ├── entities/

│       ├── users.controller.ts

│       ├── users.module.ts

│       └── users.service.ts

└── prisma/ or database/
  • Keep domain code inside feature modules.
  • Put cross-cutting filters, decorators, guards, and interceptors in common/.
  • Keep DTOs close to the module that owns them.

Bootstrap and Global Validation

async function bootstrap() {

  const app = await NestFactory.create(AppModule, { bufferLogs: true });

  app.useGlobalPipes(

    new ValidationPipe({

      whitelist: true,

      forbidNonWhitelisted: true,

      transform: true,

      transformOptions: { enableImplicitConversion: true },

    }),

  );

  app.useGlobalInterceptors(new ClassSerializerInterceptor(app.get(Reflector)));

  app.useGlobalFilters(new HttpExceptionFilter());

  await app.listen(process.env.PORT ?? 3000);

}

bootstrap();
  • Always enable whitelist and forbidNonWhitelisted on public APIs.
  • Prefer one global validation pipe instead of repeating validation config per route.

Modules, Controllers, and Providers

@Module({

  controllers: [UsersController],

  providers: [UsersService],

  exports: [UsersService],

})

export class UsersModule {}

@Controller('users')

export class UsersController {

  constructor(private readonly usersService: UsersService) {}

  @Get(':id')

  getById(@Param('id', ParseUUIDPipe) id: string) {

    return this.usersService.getById(id);

  }

  @Post()

  create(@Body() dto: CreateUserDto) {

    return this.usersService.create(dto);

  }

}

@Injectable()

export class UsersService {

  constructor(private readonly usersRepo: UsersRepository) {}

  async create(dto: CreateUserDto) {

    return this.usersRepo.create(dto);

  }

}
  • Controllers should stay thin: parse HTTP input, call a provider, return response DTOs.
  • Put business logic in injectable services, not controllers.
  • Export only the providers other modules genuinely need.

DTOs and Validation

export class CreateUserDto {

  @IsEmail()

  email!: string;

  @IsString()

  @Length(2, 80)

  name!: string;

  @IsOptional()

  @IsEnum(UserRole)

  role?: UserRole;

}
  • Validate every request DTO with class-validator.
  • Use dedicated response DTOs or serializers instead of returning ORM entities directly.
  • Avoid leaking internal fields such as password hashes, tokens, or audit columns.

Auth, Guards, and Request Context

@UseGuards(JwtAuthGuard, RolesGuard)

@Roles('admin')

@Get('admin/report')

getAdminReport(@Req() req: AuthenticatedRequest) {

  return this.reportService.getForUser(req.user.id);

}
  • Keep auth strategies and guards module-local unless they are truly shared.
  • Encode coarse access rules in guards, then do resource-specific authorization in services.
  • Prefer explicit request types for authenticated request objects.

Exception Filters and Error Shape

@Catch()

export class HttpExceptionFilter implements ExceptionFilter {

  catch(exception: unknown, host: ArgumentsHost) {

    const response = host.switchToHttp().getResponse<Response>();

    const request = host.switchToHttp().getRequest<Request>();

    if (exception instanceof HttpException) {

      return response.status(exception.getStatus()).json({

        path: request.url,

        error: exception.getResponse(),

      });

    }

    return response.status(500).json({

      path: request.url,

      error: 'Internal server error',

    });

  }

}
  • Keep one consistent error envelope across the API.
  • Throw framework exceptions for expected client errors; log and wrap unexpected failures centrally.

Config and Environment Validation

ConfigModule.forRoot({

  isGlobal: true,

  load: [configuration],

  validate: validateEnv,

});
  • Validate env at boot, not lazily at first request.
  • Keep config access behind typed helpers or config services.
  • Split dev/staging/prod concerns in config factories instead of branching throughout feature code.

Persistence and Transactions

  • Keep repository / ORM code behind providers that speak domain language.
  • For Prisma or TypeORM, isolate transactional workflows in services that own the unit of work.
  • Do not let controllers coordinate multi-step writes directly.

Testing

describe('UsersController', () => {

  let app: INestApplication;

  beforeAll(async () => {

    const moduleRef = await Test.createTestingModule({

      imports: [UsersModule],

    }).compile();

    app = moduleRef.createNestApplication();

    app.useGlobalPipes(new ValidationPipe({ whitelist: true, transform: true }));

    await app.init();

  });

});
  • Unit test providers in isolation with mocked dependencies.
  • Add request-level tests for guards, validation pipes, and exception filters.
  • Reuse the same global pipes/filters in tests that you use in production.

Production Defaults

  • Enable structured logging and request correlation ids.
  • Terminate on invalid env/config instead of booting partially.
  • Prefer async provider initialization for DB/cache clients with explicit health checks.
  • Keep background jobs and event consumers in their own modules, not inside HTTP controllers.
  • Make rate limiting, auth, and audit logging explicit for public endpoints.
BrowserAct

Let your agent run on any real-world website

Bypass CAPTCHA & anti-bot for free. Start local, scale to cloud.

Explore BrowserAct Skills →

Stop writing automation&scrapers

Install the CLI. Run your first Skill in 30 seconds. Scale when you're ready.

Start free
free · no credit card