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-rest-kit

v0.0.3

Published

A collection of decorators, filters, interceptors and utilities for building REST APIs with NestJS

Readme

nestjs-rest-kit

A collection of decorators, filters, interceptors, and utilities for building REST APIs with NestJS.

Installation

npm install nestjs-rest-kit

Requirements

  • Node.js >= 18.0.0
  • NestJS 10.x or 11.x

Features

  • Decorators - @RouteHandler, @RouteSuccessResponse, @PublicRoute, @Roles, @Permission
  • Guards - RolesGuard, PermissionGuard for RBAC
  • Filters - Standardized error response formatting
  • Interceptors - Response wrapping and error handling
  • Exceptions - RestException for unified error handling
  • JWT - JWT authentication strategy and guards

Quick Start

Basic Setup

Register HttpExceptionFilter globally in your main.ts:

// main.ts
import { NestFactory } from '@nestjs/core';
import { HttpExceptionFilter } from 'nestjs-rest-kit';
import { AppModule } from './app.module';

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

  app.useGlobalFilters(new HttpExceptionFilter());

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

Note: When using @RouteHandler decorator, interceptors (HttpResponseInterceptor, HttpErrorsInterceptor) are applied automatically per-route. Global filter handles exceptions that occur outside of route handlers.

API Reference

Decorators

@RouteHandler(path?, method?)

Combines HTTP method decorator with response/error interceptors and Swagger documentation.

import { Controller, Body, Param, Query } from '@nestjs/common';
import { ApiTags } from '@nestjs/swagger';
import { RouteHandler, RouteSuccessResponse, PublicRoute } from 'nestjs-rest-kit';

@ApiTags('Users')
@Controller('users')
export class UsersController {
  // POST is default method, no need to specify
  @RouteHandler('create')
  @RouteSuccessResponse({ description: 'Create user', type: UserDto })
  async create(@Body() body: CreateUserDto) {
    return this.service.create(body);
  }

  // GET with query params
  @RouteHandler('list', 'GET')
  @RouteSuccessResponse({ description: 'Get all users', type: [UserDto] })
  async findAll(@Query() query: QueryDto) {
    return this.service.findAll(query);
  }

  // GET with path parameter
  @RouteHandler(':id', 'GET')
  @RouteSuccessResponse({ description: 'Get user by ID', type: UserDto })
  async findOne(@Param('id') id: string) {
    return this.service.findOne(id);
  }

  // Public route (no auth required)
  @PublicRoute()
  @RouteHandler('health', 'GET')
  healthCheck() {
    return { status: 'ok' };
  }
}

Parameters:

  • path - Route path (optional)
  • method - HTTP method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' (default: 'POST')

@RouteSuccessResponse(options?)

Swagger decorator that wraps response type in SuccessResponseDto.

import { RouteSuccessResponse } from 'nestjs-rest-kit';

@RouteSuccessResponse({ type: UserDto, description: 'User found' })
findOne() { ... }

@PublicRoute()

Marks a route as public (no JWT authentication required). Use with JwtAuthGuard to exclude specific routes from authentication.

import { Body } from '@nestjs/common';
import { RouteHandler, RouteSuccessResponse, PublicRoute } from 'nestjs-rest-kit';

// This route is accessible without authentication
@PublicRoute()
@RouteHandler('register')
@RouteSuccessResponse({ type: UserDto })
register(@Body() dto: RegisterDto) {
  return this.authService.register(dto);
}

@Roles(...roles)

Restricts access to users with specific roles. Automatically applies RolesGuard.

import { Roles } from 'nestjs-rest-kit';

// Require any of these roles (OR logic)
@Roles('admin', 'moderator')
@RouteHandler('users', 'DELETE')
deleteUser() { ... }

Role checking logic:

  1. If super: true in JWT → access granted (bypass)
  2. If role (string) in JWT → strict match against required roles
  3. If roles (array) in JWT → any match grants access

Multiple decorators use AND logic:

@Roles('admin')
@Roles('verified')  // Must have admin AND verified

@Permission(subject, action)

Restricts access based on permissions. Automatically applies PermissionGuard.

Permissions in JWT should be in subject:action format. Supports wildcards.

import { Permission } from 'nestjs-rest-kit';

// Require single permission
@Permission('users', 'delete')
@RouteHandler('users/:id', 'DELETE')
deleteUser() { ... }

// Require any of these actions (OR logic)
@Permission('users', ['read', 'write'])
@RouteHandler('users', 'GET')
getUsers() { ... }

Wildcards:

// JWT: { permissions: ['admin:*'] }
// Matches: admin:read, admin:write, admin:delete, etc.

Multiple decorators use AND logic:

@Permission('users', 'read')
@Permission('audit', 'read')  // Must have BOTH permissions

@AnyPermission(...permissions)

Allows access if user has ANY of the specified permissions (OR logic between different subjects).

import { AnyPermission } from 'nestjs-rest-kit';

// User needs 'users:delete' OR 'posts:delete'
@AnyPermission(['users', 'delete'], ['posts', 'delete'])
@RouteHandler('content/:id', 'DELETE')
deleteContent() { ... }

Guards

RolesGuard

Checks user roles against @Roles decorator requirements. Applied automatically by @Roles.

PermissionGuard

Checks user permissions against @Permission and @AnyPermission decorator requirements. Applied automatically.

Interceptors

HttpResponseInterceptor

Wraps successful responses in a standardized format:

{
  "success": true,
  "response": { ... },
  "timestamp": 1705931234567
}

HttpErrorsInterceptor

Handles errors and wraps them in a standardized format. Logs 5xx errors and hides internal details.

{
  "success": false,
  "error": {
    "statusCode": 400,
    "message": "Validation failed",
    "errorCode": "VALIDATION_ERROR",
    "errors": {
      "email": ["must be a valid email"]
    }
  },
  "timestamp": 1705931234567
}

Security: For 5xx errors, the message is replaced with "Internal server error" and errors field is omitted to prevent leaking internal details.

Filters

HttpExceptionFilter

Catches HttpException and formats the response using ErrorResponseDto.

// main.ts
import { NestFactory } from '@nestjs/core';
import { HttpExceptionFilter } from 'nestjs-rest-kit';

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

  app.useGlobalFilters(new HttpExceptionFilter());

  await app.listen(3000);
}

Exceptions

RestException

Universal exception class that normalizes any error input into a standardized format.

import { RestException } from 'nestjs-rest-kit';

// From string
throw new RestException('Something went wrong');

// From object
throw new RestException({
  message: 'User not found',
  statusCode: 404,
  errorCode: 'USER_NOT_FOUND',
});

// From HttpException (import { BadRequestException } from '@nestjs/common')
throw new RestException(new BadRequestException('Invalid input'));

// With validation errors
throw new RestException({
  message: 'Validation failed',
  statusCode: 400,
  errorCode: 'VALIDATION_ERROR',
  errors: {
    email: ['Invalid email format'],
    password: ['Too short'],
  },
});

DTOs

SuccessResponseDto<T>

class SuccessResponseDto<T> {
  success: boolean;    // always true
  response?: T;        // response payload
  timestamp: number;   // Unix timestamp in ms
}

ErrorResponseDto

class ErrorResponseDto {
  success: boolean;        // always false
  error: ErrorDetailsDto;  // error details
  timestamp: number;       // Unix timestamp in ms
}

ErrorDetailsDto

class ErrorDetailsDto {
  message: string;                // Error message
  statusCode?: HttpStatus;        // HTTP status code
  errorCode?: string;             // Application-specific error code
  errors?: ValidationErrorsDto;   // Validation errors by field
}

ValidationErrorsDto

class ValidationErrorsDto {
  [key: string]: string[] | ValidationErrorsDto;  // Nested structure supported
}

JWT Authentication

Setup

import { Module } from '@nestjs/common';
import { APP_GUARD } from '@nestjs/core';
import { PassportModule } from '@nestjs/passport';
import { createJwtStrategyProvider, JwtAuthGuard } from 'nestjs-rest-kit';

@Module({
  imports: [PassportModule],
  providers: [
    createJwtStrategyProvider(process.env.JWT_SECRET),
    {
      provide: APP_GUARD,
      useClass: JwtAuthGuard,
    },
  ],
})
export class AuthModule {}

JwtAuthGuard

Global guard that protects all routes by default. Use @PublicRoute() to exclude specific routes.

import { Controller, Body } from '@nestjs/common';
import { RouteHandler, RouteSuccessResponse, PublicRoute } from 'nestjs-rest-kit';

@Controller('auth')
export class AuthController {
  // Protected: requires valid JWT token
  @RouteHandler('profile', 'GET')
  @RouteSuccessResponse({ type: ProfileDto })
  getProfile() {
    return this.authService.getProfile();
  }

  // Public: accessible without authentication
  @PublicRoute()
  @RouteHandler('login')
  @RouteSuccessResponse({ type: TokenDto })
  login(@Body() dto: LoginDto) {
    return this.authService.login(dto);
  }
}

createJwtStrategyProvider(secret)

Factory function to create JWT strategy provider.

import { createJwtStrategyProvider } from 'nestjs-rest-kit';

// In your module providers:
createJwtStrategyProvider('your-jwt-secret')

Interfaces

JwtPayloadInterface

interface JwtPayloadInterface {
  iss: string;
  sub: string;
  iat?: number;
  exp?: number;
  user: string;
  appId?: string;

  // Role-based access control
  role?: string;         // Single role (strict match, takes precedence)
  roles?: string[];      // Multiple roles (any match)

  // Permission-based access control
  permissions?: string[]; // Format: 'subject:action', supports wildcards ('admin:*')

  // Super user bypass
  super?: boolean;        // Bypasses all role and permission checks
}

Full Example

// main.ts
import { NestFactory } from '@nestjs/core';
import { HttpExceptionFilter } from 'nestjs-rest-kit';
import { AppModule } from './app.module';

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

  app.useGlobalFilters(new HttpExceptionFilter());

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

// app.module.ts
import { Module } from '@nestjs/common';
import { APP_GUARD } from '@nestjs/core';
import { PassportModule } from '@nestjs/passport';
import { createJwtStrategyProvider, JwtAuthGuard } from 'nestjs-rest-kit';
import { UsersModule } from './users/users.module';

@Module({
  imports: [PassportModule, UsersModule],
  providers: [
    createJwtStrategyProvider(process.env.JWT_SECRET),
    {
      provide: APP_GUARD,
      useClass: JwtAuthGuard,
    },
  ],
})
export class AppModule {}

// users.controller.ts
import { Controller, Body, Param, Query } from '@nestjs/common';
import { ApiTags } from '@nestjs/swagger';
import {
  RouteHandler,
  RouteSuccessResponse,
  PublicRoute,
  Roles,
  Permission,
  RestException,
} from 'nestjs-rest-kit';

@ApiTags('Users')
@Controller('users')
export class UsersController {
  // Protected route (requires authentication)
  @Permission('users', 'read')
  @RouteHandler('list', 'GET')
  @RouteSuccessResponse({ description: 'Get all users', type: [UserDto] })
  async findAll(@Query() query: QueryDto) {
    return this.usersService.findAll(query);
  }

  // Protected route with path parameter
  @Permission('users', 'read')
  @RouteHandler(':id', 'GET')
  @RouteSuccessResponse({ description: 'Get user by ID', type: UserDto })
  async findOne(@Param('id') id: string) {
    const user = await this.usersService.findOne(id);
    if (!user) {
      throw new RestException({
        message: 'User not found',
        statusCode: 404,
        errorCode: 'USER_NOT_FOUND',
      });
    }
    return user;
  }

  // Admin only route
  @Roles('admin')
  @Permission('users', 'delete')
  @RouteHandler(':id', 'DELETE')
  @RouteSuccessResponse({ description: 'Delete user' })
  async delete(@Param('id') id: string) {
    return this.usersService.delete(id);
  }

  // Public route (no auth required)
  @PublicRoute()
  @RouteHandler('register')
  @RouteSuccessResponse({ description: 'Register new user', type: UserDto })
  async register(@Body() dto: CreateUserDto) {
    return this.usersService.register(dto);
  }
}

License

MIT