← BACK

nestjs-boilerplate

A reusable NestJS starter template with essential backend configurations, best practices, and development tools.

TypeScript ⭐ 0

NestJS Base

A reusable NestJS 11 base project with conventions and common infrastructure for building multiple applications. This repository focuses on the application's core foundation, including environment configuration, validation, exception handling, logging, Swagger documentation, and API security.

Features

Area Technology / Approach
Environment & Configuration @nestjs/config, Joi, and typed configuration namespaces
Validation & DTOs class-validator, class-transformer, and a global ValidationPipe
Exception Handling Global exception filter, stable error codes, and a standardized error envelope
Logging nestjs-pino + pino-http, request IDs, and sensitive data redaction
Swagger & API Conventions /docs, standardized response envelopes, and reusable decorators for success, error, and pagination responses
Security & API Protection Helmet, CORS, rate limiting, JWT guard, role guard, and @Public()

Getting Started

Prerequisites

  • Node.js
  • npm

Installation

# 1. Install dependencies
npm install

# 2. Create the environment file
cp .env.example .env

# 3. Generate a secure JWT secret (at least 32 characters)
node -e "console.log(require('crypto').randomBytes(48).toString('hex'))"

Update JWT_SECRET in your .env file with the generated value.

Run the Application

# Start in development mode
npm run start:dev

Available Endpoints

Resource URL
API Base URL http://localhost:3000/api/v1
Health Check http://localhost:3000/health
Swagger UI http://localhost:3000/docs
OpenAPI JSON http://localhost:3000/docs-json

Available Scripts

npm run build
npm run start:prod
npm run lint
npm run format
npm test
npm run test:e2e

Project Structure

src/
├── app.module.ts                 # Wires all global providers
├── app.controller.ts
├── app.service.ts
├── main.ts                       # Bootstrap: Helmet, CORS, prefix, versioning, Swagger
├── config/
│   ├── app.config.ts             # "app" configuration namespace
│   ├── auth.config.ts            # "auth" configuration namespace
│   ├── logging.config.ts         # "logging" configuration namespace
│   ├── swagger.config.ts         # "swagger" configuration namespace
│   ├── throttler.config.ts       # "throttler" configuration namespace
│   ├── env.validation.ts         # Joi schema and fail-fast environment validation
│   └── setup-swagger.ts
├── common/
│   ├── decorators/               # @Public, @Roles, @CurrentUser, Swagger decorators
│   ├── dto/                      # Pagination and response/error envelope DTOs
│   ├── enums/
│   ├── errors/                   # AppException + ErrorCode
│   ├── filters/                  # AllExceptionsFilter
│   ├── guards/                   # JwtAuthGuard, RolesGuard
│   ├── interceptors/             # ResponseTransformInterceptor
│   ├── interfaces/
│   └── validation/               # ValidationPipe factory and error flattening
└── modules/
    ├── auth/                     # JWT authentication + demo login
    └── health/                   # GET /health

Environment Variables

See .env.example for the complete list of environment variables.

Variable Default Description
NODE_ENV development Supported values: development, test, production
PORT 3000 Application port
API_PREFIX api API prefix. Final route format: /{prefix}/v{version}
API_VERSION 1 URI versioning
CORS_ORIGINS * Comma-separated list of allowed origins
JWT_SECRET Required Minimum 32 characters. The application will not start if missing
JWT_EXPIRES_IN 1h JWT expiration time
THROTTLE_TTL_MS 60000 Rate-limiting window in milliseconds
THROTTLE_LIMIT 100 Maximum requests per IP per window
LOG_LEVEL info Log level, from fatal to trace
LOG_PRETTY true in development Set to false in production to output JSON logs
SWAGGER_ENABLED true Enables or disables Swagger documentation
DEMO_AUTH_ENABLED true in development Automatically disabled in production

Environment variables are validated during application bootstrap. If a required variable is missing or invalid, the application reports the affected variable and exits instead of continuing with an invalid configuration.

API Conventions

URL Structure

/api/v1/...    # Versioned API routes
/health        # Health check, without prefix or version
/docs          # Swagger UI

Success Response Envelope

All successful responses are wrapped by ResponseTransformInterceptor.

Example:

{
  "success": true,
  "data": {
    "id": "..."
  },
  "requestId": "6a4f...",
  "timestamp": "2026-01-01T00:00:00.000Z"
}

For endpoints that need to return raw responses, such as files, streams, or webhooks, use @SkipResponseTransform().

HTTP 204 No Content responses are also preserved without transformation.

Error Response Envelope

All exceptions are handled by AllExceptionsFilter and normalized into a consistent error structure.

Example:

{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Validation failed.",
    "details": [
      {
        "field": "email",
        "messages": ["email must be an email"]
      }
    ]
  },
  "requestId": "6a4f...",
  "path": "/api/v1/auth/login",
  "method": "POST",
  "timestamp": "2026-01-01T00:00:00.000Z"
}

Clients should rely on error.code rather than the human-readable message.

The list of supported error codes is defined in src/common/errors/error-code.ts.

Server-side errors (HTTP 5xx) return a generic message to prevent internal details and stack traces from being exposed.

Request ID

pino-http generates an x-request-id for each request or reuses the ID provided by the client.

The request ID is included in:

  • The response header
  • The response envelope
  • Application logs

This makes it easier to trace requests across application logs.

Validation & DTOs

The global ValidationPipe enables the following options:

  • whitelist: Removes properties that do not have validation decorators.
  • forbidNonWhitelisted: Rejects requests containing unexpected properties.
  • transform: Automatically transforms incoming payloads into DTO instances.

Requests containing unexpected fields are rejected with a VALIDATION_ERROR.

DTO Example

export class CreateUserDto {
  @ApiProperty({ example: '[email protected]' })
  @IsEmail()
  email!: string;

  @ApiProperty({ minLength: 8 })
  @IsString()
  @MinLength(8)
  password!: string;
}

Pagination

Reusable pagination DTOs and response decorators are provided.

@Get()
@ApiPaginatedEnvelopeResponse(UserDto)
findAll(@Query() query: PaginationQueryDto) {
  return new PaginatedResponseDto(items, total, query);
}

Use these conventions to keep pagination behavior and API documentation consistent across modules.

Exception Handling

Use AppException to represent business-level errors.

Example:

throw new AppException(
  ErrorCode.CONFLICT,
  'Email already exists.',
  HttpStatus.CONFLICT,
);

AppException accepts:

  • code: A stable application error code
  • message: A human-readable error message
  • HTTP status
  • Optional details

AllExceptionsFilter converts the exception into the standardized error envelope.

Logging

The application uses nestjs-pino and pino-http for structured logging.

  • Development: Colorized, human-readable logs through pino-pretty.
  • Production: JSON logs suitable for collection by Loki, ELK, Datadog, and similar platforms.
  • Automatic request logging: Includes HTTP method, path, status code, response time, and request ID.
  • Sensitive data redaction: Headers such as authorization, cookie, and set-cookie are redacted.
  • Health check exclusion: /health requests are excluded from logging to reduce noise.
  • Service-level logging: Inject PinoLogger from nestjs-pino, or use NestJS's Logger, which is routed through Pino in main.ts.

Swagger Documentation

Access the interactive API documentation at /docs.

Swagger is configured with the following features:

  • Bearer authentication using the access-token security scheme.
  • An Authorize button for supplying JWT access tokens.
  • persistAuthorization to retain authorization credentials when reloading the page.
  • Reusable decorators for documenting standardized responses.

Available Response Decorators

Decorator Purpose
@ApiOkEnvelopeResponse(Model) Documents a successful HTTP 200 response
@ApiCreatedEnvelopeResponse(Model) Documents a successful HTTP 201 response
@ApiPaginatedEnvelopeResponse(Model) Documents a paginated response
@ApiCommonErrorResponses() Documents common API error responses

Example:

@Get(':id')
@ApiOkEnvelopeResponse(UserDto)
@ApiCommonErrorResponses()
findOne(@Param('id') id: string) {}

These decorators help keep the Swagger documentation aligned with the actual API response format.

Security

The base project includes several security mechanisms.

  • Helmet: Enables HTTP security headers. Content Security Policy (CSP) is temporarily disabled while Swagger is enabled because Swagger UI uses inline scripts. Disable Swagger in production to allow CSP to be enabled again, according to the application's configuration.
  • CORS: Configurable through CORS_ORIGINS, with credentials support.
  • Rate limiting: Applied globally through @nestjs/throttler. Login requests have a stricter limit of five requests per minute.
  • JWT guard: Applied globally. All routes require a Bearer token by default unless explicitly marked with @Public().
  • Role-based access control: Use @Roles(Role.ADMIN) to restrict access based on user roles.
  • Current user access: Use @CurrentUser() user: AuthenticatedUser to retrieve the authenticated user, or @CurrentUser('sub') userId: string to retrieve the subject ID.
  • Trust proxy: Set TRUST_PROXY=true when running behind a reverse proxy so rate limiting can use the correct client IP address.

Demo Login

The base project includes a demo login flow in AuthService, configured through DEMO_AUTH_EMAIL and DEMO_AUTH_PASSWORD.

This allows you to test authentication without integrating a user database immediately.

Example login request:

curl -X POST http://localhost:3000/api/v1/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email":"[email protected]","password":"Admin@123456"}'

After obtaining an access token, call a protected endpoint:

curl http://localhost:3000/api/v1/auth/me \
  -H "Authorization: Bearer <accessToken>"

The credentials above are illustrative defaults; use the values configured in your .env file.

Before using this base project in a real application, replace the demo credential verification logic in src/modules/auth/auth.service.ts with a proper user store and secure password hashing, such as Argon2 or bcrypt.

Demo authentication is automatically disabled in production unless DEMO_AUTH_ENABLED=true is explicitly configured.

Adding a New Module

Organize new features under src/modules/ using a consistent module structure.

Example: adding a users module.

src/modules/users/
├── dto/
│   └── create-user.dto.ts
├── users.controller.ts
├── users.module.ts
└── users.service.ts

Controllers are protected by the global JWT guard by default.

When implementing new endpoints:

  1. Use @Public() only for endpoints that should be publicly accessible.
  2. Use @Roles(...) for endpoints that require role-based authorization.
  3. Use the provided Swagger decorators to document success and error responses consistently.
  4. Follow the existing DTO, validation, exception-handling, and response-envelope conventions.

These conventions help maintain consistency as the application grows and make the base project easier to reuse across different services.