npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

nestjs-api-forge

v1.0.7

Published

Standardized API response formatting, error handling, and exception filters for NestJS applications

Readme

⚡ NestJS API Forge

Plug-and-play response envelope, exception filter, and error formatting for NestJS REST APIs — zero boilerplate, fully typed.

npm version npm downloads License: MIT NestJS Built with TypeScript CI Sponsor Buy Me a Coffee

One import to standardize every response, every error, and every validation in your NestJS API.


🧩 What it does

Register ApiForgeModule once and every response from your API is automatically shaped into a consistent envelope. No more manual { success, data, message } wrappers scattered across controllers.

| Layer | What Forge provides | |-------|-------------------| | 📦 | Response envelope — every handler wrapped with success, statusCode, message, data, and meta | | 🛡️ | Global exception filter — handles HttpException, validation errors, and unexpected crashes uniformly | | ✅ | Structured validationForgeValidationPipe replaces ValidationPipe with typed field-level error details | | 📄 | Paginated responsesApiResponseDto.paginated() with full pagination meta in one call | | 🏷️ | Per-route decorators — override messages, skip wrapping, add custom meta, or mark routes deprecated | | 🔍 | Request tracing — correlation ID passthrough, auto UUID generation, and response time measurement | | 💥 | Typed exceptions — drop-in replacements for every NestJS HttpException with structured error codes |

Decorators at a glance

| Decorator | Scope | Description | |-----------|-------|-------------| | @ApiForge(options?) | Controller / Method | Apply filter + interceptor without going global | | @ForgeMessage(msg) | Controller / Method | Override the success message for that route | | @ForgeRawResponse() | Controller / Method | Skip envelope wrapping — return raw handler value | | @ForgeMeta(extra) | Controller / Method | Merge extra key-value pairs into meta | | @ForgeDeprecated(notice?) | Controller / Method | Mark route deprecated — adds meta.deprecated + Deprecation header |


📦 Installation

Requirements

  • Node.js v18 or higher
  • NestJS v9, v10, or v11

Install

npm install nestjs-api-forge

Verify the install:

node -e "require('nestjs-api-forge'); console.log('nestjs-api-forge installed')"

🚀 Quick Start

1. Register globally in app.module.ts

import { Module } from '@nestjs/common';
import { ApiForgeModule } from 'nestjs-api-forge';

@Module({
  imports: [
    ApiForgeModule.forRoot({
      version: '1.0.0',
      defaultSuccessMessage: 'Request successful',
      includePath: true,
      includeTimestamp: true,
      includeRequestId: true,
      includeResponseTime: true,
      correlationIdHeader: 'x-request-id',
    }),
  ],
})
export class AppModule {}

2. Add ForgeValidationPipe in main.ts

import { NestFactory } from '@nestjs/core';
import { ForgeValidationPipe } from 'nestjs-api-forge';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalPipes(new ForgeValidationPipe());
  await app.listen(3000);
}
bootstrap();

That's it. Every response is now wrapped, every error is structured, and every validation failure returns typed field details.


📐 Response Shapes

Success (2xx)

{
  "success": true,
  "statusCode": 200,
  "message": "User fetched successfully",
  "data": { "id": 1, "name": "Alice Johnson", "email": "[email protected]" },
  "meta": {
    "timestamp": "2025-01-15T10:00:00.000Z",
    "path": "/api/users/1",
    "version": "1.0.0",
    "requestId": "a3f2c1d0-84e5-4b6a-9123-abc123def456",
    "responseTime": "4ms"
  }
}

Error (4xx / 5xx)

{
  "success": false,
  "statusCode": 404,
  "message": "User not found",
  "error": { "code": "NOT_FOUND" },
  "meta": {
    "timestamp": "2025-01-15T10:00:00.000Z",
    "path": "/api/users/99",
    "requestId": "b1e2f3a4-0000-4b5c-8d9e-fedcba987654",
    "responseTime": "2ms"
  }
}

Validation Error

{
  "success": false,
  "statusCode": 400,
  "message": "Validation failed",
  "error": {
    "code": "VALIDATION_ERROR",
    "details": [
      { "field": "email", "message": "must be an email" },
      { "field": "address.zip", "message": "must be a string" }
    ]
  },
  "meta": { "timestamp": "2025-01-15T10:00:00.000Z", "path": "/api/users" }
}

Paginated

{
  "success": true,
  "statusCode": 200,
  "message": "Users fetched successfully",
  "data": [{ "id": 1, "name": "Alice Johnson" }],
  "pagination": {
    "total": 42,
    "page": 2,
    "limit": 10,
    "totalPages": 5,
    "hasNextPage": true,
    "hasPrevPage": true
  },
  "meta": {
    "timestamp": "2025-01-15T10:00:00.000Z",
    "path": "/api/users?page=2&limit=10",
    "responseTime": "8ms"
  }
}

Deprecated Route

{
  "success": true,
  "statusCode": 200,
  "message": "OK",
  "data": {},
  "meta": {
    "timestamp": "2025-01-15T10:00:00.000Z",
    "deprecated": true,
    "deprecationNotice": "Use /v2/users instead"
  }
}

📖 API Reference

ApiForgeModule.forRoot(options?)

| Option | Type | Default | Description | |--------|------|---------|-------------| | includePath | boolean | true | Include request path in meta | | includeTimestamp | boolean | true | Include ISO timestamp in meta | | includeRequestId | boolean | false | Auto-generate UUID requestId in meta if no correlation header found | | correlationIdHeader | string \| string[] | ['x-request-id', 'x-correlation-id'] | Header(s) to read request ID from; echoed back on the response | | includeResponseTime | boolean | false | Include handler duration as meta.responseTime (e.g. "12ms") | | version | string | undefined | API version string added to meta | | defaultSuccessMessage | string | 'Request successful' | Fallback success message |

ApiForgeModule.forRootAsync(asyncOptions)

Use when options depend on a config service:

ApiForgeModule.forRootAsync({
  imports: [ConfigModule],
  inject: [ConfigService],
  useFactory: (config: ConfigService) => ({
    version: config.get('API_VERSION'),
    includeRequestId: true,
    includeResponseTime: true,
    correlationIdHeader: config.get('CORRELATION_HEADER'),
  }),
})

ForgeValidationPipe

Drop-in replacement for NestJS ValidationPipe. Throws ValidationException with structured details instead of a string-message array. Supports nested objects using dot-notation field paths.

// Use defaults
app.useGlobalPipes(new ForgeValidationPipe());

// Override defaults
app.useGlobalPipes(new ForgeValidationPipe({
  whitelist: false,
  forbidNonWhitelisted: false,
}));

ApiResponseDto — manual builders

import { ApiResponseDto } from 'nestjs-api-forge';

ApiResponseDto.success(data, 'OK', 200, { path: '/users/1' });
ApiResponseDto.created(data, 'User created');
ApiResponseDto.accepted(jobRef, 'Export queued');   // 202
ApiResponseDto.noContent('Deleted');                // 204
ApiResponseDto.paginated(data, total, page, limit, 'Users fetched');
ApiResponseDto.error('Not found', 404, { code: 'NOT_FOUND' });

Built-in Exceptions

All extend ApiExceptionHttpException. Pass an optional message and details array to any of them.

| Class | Status | Code | |-------|--------|------| | BadRequestException | 400 | BAD_REQUEST | | UnauthorizedException | 401 | UNAUTHORIZED | | PaymentRequiredException | 402 | PAYMENT_REQUIRED | | ForbiddenException | 403 | FORBIDDEN | | NotFoundException | 404 | NOT_FOUND | | MethodNotAllowedException | 405 | METHOD_NOT_ALLOWED | | ConflictException | 409 | CONFLICT | | UnprocessableEntityException | 422 | UNPROCESSABLE_ENTITY | | TooManyRequestsException | 429 | TOO_MANY_REQUESTS | | InternalServerException | 500 | INTERNAL_SERVER_ERROR | | ServiceUnavailableException | 503 | SERVICE_UNAVAILABLE | | GatewayTimeoutException | 504 | GATEWAY_TIMEOUT | | ValidationException | 400 | VALIDATION_ERROR |


💡 Usage Examples

Standard CRUD controller

import { Controller, Get, Delete, Param, HttpCode, HttpStatus } from '@nestjs/common';
import { ForgeMessage, NotFoundException } from 'nestjs-api-forge';

@Controller('users')
export class UsersController {
  @Get(':id')
  @ForgeMessage('User fetched successfully')
  findOne(@Param('id') id: number) {
    const user = this.usersService.findById(id);
    if (!user) throw new NotFoundException('User not found');
    return user;
  }

  @Delete(':id')
  @HttpCode(HttpStatus.NO_CONTENT)
  remove(@Param('id') id: number) {
    this.usersService.remove(id);
  }
}

Paginated list

@Get()
@ForgeRawResponse()
findAll(@Query('page') page = '1', @Query('limit') limit = '10') {
  const p = parseInt(page, 10);
  const l = parseInt(limit, 10);
  const { data, total } = this.usersService.findAll(p, l);
  return ApiResponseDto.paginated(data, total, p, l, 'Users fetched');
}

Deprecated route

@Get('export')
@ForgeDeprecated('Use POST /v2/users/export instead')
@ForgeMessage('Export started')
startExport() {
  const job = this.usersService.queueExport();
  return ApiResponseDto.accepted({ jobId: job.id }, 'Export queued');
}

Custom meta per route

@Get()
@ForgeMeta({ region: 'us-east-1', cache: 'miss' })
findAll() {
  return this.usersService.findAll();
}

Per-controller scope (no global module)

@Controller('products')
@ApiForge({ version: '2.0', includeResponseTime: true })
export class ProductsController { ... }

Raw response (health check)

@Get('health')
@ForgeRawResponse()
health() {
  return { status: 'ok', uptime: process.uptime() };
}

Correlation ID tracing

Send X-Request-ID: abc-123 in the request — the same ID appears in meta.requestId and is echoed in the response header. Useful for distributed tracing across microservices.

ApiForgeModule.forRoot({
  correlationIdHeader: 'x-request-id',   // or an array of headers
  includeRequestId: true,                // generate UUID if header is absent
})

Custom exception with field details

throw new BadRequestException('Invalid input', [
  { field: 'price', message: 'Must be a positive number', value: -5 },
]);

// From class-validator constraint map
throw ValidationException.fromConstraints({
  email: { isEmail: 'must be an email' },
  age: { min: 'must be at least 1' },
});

📁 Project Structure

nestjs-api-forge/
├── src/
│   ├── api-forge.module.ts         # forRoot / forRootAsync registration
│   ├── index.ts                    # Public exports
│   ├── decorators/
│   │   └── api-response.decorator.ts  # @ApiForge, @ForgeMessage, @ForgeRawResponse, @ForgeMeta, @ForgeDeprecated
│   ├── dto/
│   │   └── api-response.dto.ts        # ApiResponseDto static builders
│   ├── exceptions/
│   │   ├── api.exception.ts           # Base ApiException class
│   │   └── validation.exception.ts    # ValidationException with field details
│   ├── filters/
│   │   └── global-exception.filter.ts # ForgeExceptionFilter
│   ├── interceptors/
│   │   └── response.interceptor.ts    # ForgeResponseInterceptor
│   ├── interfaces/
│   │   └── api-response.interface.ts  # TypeScript interfaces and ForgeOptions
│   ├── pipes/
│   │   └── forge-validation.pipe.ts   # ForgeValidationPipe
│   └── utils/
│       └── error-code.util.ts
├── app/                            # Example NestJS app demonstrating the library
│   └── src/
│       ├── users/                  # Full CRUD example with pagination
│       └── products/               # Per-controller @ApiForge example
├── .github/
│   ├── ISSUE_TEMPLATE/            # Bug report & feature request templates
│   └── workflows/
│       ├── publish.yml            # npm publish on tag push
│       └── malware-scan.yml       # Security scan on every push
├── CONTRIBUTING.md
├── CODE_OF_CONDUCT.md
└── package.json

🛠️ Development

# Clone the repo
git clone https://github.com/mirzasaikatahmmed/nestjs-api-forge.git
cd nestjs-api-forge

# Install dependencies
npm install

# Build the library
npm run build

# Watch mode
npm run build:watch

# Format code
npm run format

# Run the example app
cd app && npm install && npm run start:dev

📦 Peer Dependencies

@nestjs/common   ^9 | ^10 | ^11
@nestjs/core     ^9 | ^10 | ^11
reflect-metadata ^0.1 | ^0.2
rxjs             ^7

📋 Changelog

v1.0.7 — Current

  • 10 new HTTP exception classesNotAcceptableException (406), RequestTimeoutException (408), GoneException (410), PayloadTooLargeException (413), UnsupportedMediaTypeException (415), LockedException (423), FailedDependencyException (424), PreconditionRequiredException (428), NotImplementedException (501), BadGatewayException (502)
  • ForgeHealthModule — configurable /health endpoint with custom checks, uptime, memory stats, and Forge-formatted responses; throws 503 automatically when any check fails
  • Updated error-code map — added LOCKED, FAILED_DEPENDENCY, PRECONDITION_REQUIRED mappings

Full history: CHANGELOG.md

v1.0.6

  • Changelog updated — all versions documented with accurate entries aligned to npm release history

v1.0.5

  • Open source documentation — full README rewrite with badges, "What it does" table, response shape examples, and complete API reference
  • Community health filesCONTRIBUTING.md, CODE_OF_CONDUCT.md, CONTRIBUTORS.md, REPOSITORY_RULES.md
  • GitHub templates — bug report and feature request issue templates, FUNDING.yml, CODEOWNERS
  • ESLint setup — ESLint v10 flat config (eslint.config.mjs) with @typescript-eslint integration; npm run lint now works out of the box

v1.0.4

  • ForgeValidationPipe — structured validation errors with nested field support (dot-notation)
  • @ForgeMeta(extra) — merge custom fields into response meta per route or controller
  • @ForgeDeprecated(notice?) — deprecation flag in meta + Deprecation: true response header
  • @ForgeMessage, @ForgeRawResponse, @ApiForge decorators
  • ApiResponseDto.accepted() — 202 Accepted helper
  • Correlation ID passthrough (correlationIdHeader option)
  • Response time measurement (includeResponseTime option)
  • forRootAsync fix — options factory now runs once instead of twice
  • 3 new exceptions: MethodNotAllowedException, PaymentRequiredException, GatewayTimeoutException
  • Response metadata support (includePath, includeTimestamp, includeRequestId, includeResponseTime)

v1.0.3

  • Update repository URL format in package.json

v1.0.0

  • Initial release — ApiForgeModule.forRoot(), ForgeExceptionFilter, ForgeResponseInterceptor, ApiResponseDto, and 10 typed exceptions

🤝 Contributing

Pull requests are welcome! For major changes, please open an issue first to discuss your approach.

  1. Fork the repository
  2. Create your branch: git checkout -b feat/your-feature
  3. Make your changes and run npm run format
  4. Push and open a PR against main

Please read CONTRIBUTING.md for full guidelines and CODE_OF_CONDUCT.md before participating. All contributors are listed in CONTRIBUTORS.md.


💛 Support

If NestJS API Forge saves you boilerplate, consider supporting the project:

GitHub Sponsors Buy Me a Coffee


Made with ❤️ by Mirza Saikat Ahmmed