@denis_bruns/nestjs-route-handler v0.1.3
@denis_bruns/nestjs-route-handler
A flexible NestJS route handler builder that offers JSON Schema validation, async/Observable flows, and various configurations.
Overview
@denis_bruns/nestjs-route-handler
brings together NestJS, RxJS, and AJV-based JSON Schema validation to simplify server request handling. It helps you:
- Validate request bodies against JSON Schemas at runtime
- Reflect initial body or query parameters using
@denis_bruns/reflection
- Build async or Observable-based flows using your own “use cases” or inline handler functions
- Generate consistent CORS and security headers out of the box
- Define custom error mappings (HTTP status codes for particular error classes)
- Enforce a payload size limit and optional request timeout to guard against resource hogs
This library is particularly useful in a clean architecture or onion architecture context, where you separate concerns into use cases or interactors that run within each request.
Key Features
Inline “Use Case” Functions
- Provide a function that returns an Observable or Promise to handle the request logic.
- Chain multiple handlers in sequence to build complex flows.
JSON Schema Validation
- Validate request bodies using
ajv
for robust error reporting. - Provide your schema in the route config (
bodySchema
).
- Validate request bodies using
Reflectors for Body and Query
- Use
reflect
from@denis_bruns/reflection
to transform request data or extract partial info from nested structures.
- Use
CORS & Security Headers
- Built-in CORS origin whitelisting.
- Default security headers like
Strict-Transport-Security
,X-Frame-Options
, etc.
Timeout & Payload Size Limits
timeoutMs
option triggersRequestTimeoutError
if the use case doesn’t resolve in time.maxResponseSize
ensures final JSON response isn’t too large, returningPayloadTooLargeError
if exceeded.
Custom Error-to-Status Mappings
- Map your custom or built-in error classes to specific HTTP status codes.
- Example:
CustomNotFoundError -> 404
,CustomAuthError -> 401
, etc.
Installation
With npm:
npm install @denis_bruns/nestjs-route-handler
Or with yarn:
yarn add @denis_bruns/nestjs-route-handler
You also need NestJS and Express (or a Nest platform), plus any optional libraries you want:
npm install ajv ajv-formats ajv-errors rxjs express
Basic Usage
Below is a simple NestJS controller example using nestJsRouteHandlerBuilder
:
// user.controller.ts
import { Controller, Post, Req, Res, Next } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
import { nestJsRouteHandlerBuilder } from '@denis_bruns/nestjs-route-handler';
import { IUseCaseInlineFunc, IJsonSchema } from '@denis_bruns/core';
interface UserDTO {
email: string;
name: string;
password: string;
}
interface CreatedUser {
id: string;
name: string;
}
// 1) Define your AJV JSON schema
const userSchema: IJsonSchema = {
type: 'object',
properties: {
email: { type: 'string', format: 'email' },
name: { type: 'string', minLength: 2 },
password: { type: 'string', minLength: 8 }
},
required: ['email', 'name', 'password'],
additionalProperties: false
};
// 2) Example "use case" inline function
const createUserUseCase: IUseCaseInlineFunc<UserDTO, UserDTO, CreatedUser> = (query) => ({
execute: () => {
// your create user logic here...
return Promise.resolve({ id: '123', name: query.data?.name! });
}
});
@Controller('user')
export class UserController {
// 3) Build your route handler
private readonly createUserHandler = nestJsRouteHandlerBuilder<
UserDTO, // The shape for the initial query
[typeof createUserUseCase] // A tuple of inline functions
>({
// A "reflector" describing how to pick up data from the request body
initialQueryReflector: {
data: {
email: "$['body']['email']",
name: "$['body']['name']",
password: "$['body']['password']"
}
},
handlers: [createUserUseCase],
bodySchema: userSchema, // JSON Schema for request body
timeoutMs: 3000, // optional, throws RequestTimeoutError if over 3s
errorToStatusCodeMapping: {
400: [], // schema errors default to 400
404: [],
// ... custom errors
}
}, {
// Handler options
corsOriginWhitelist: ['https://mydomain.com'], // or undefined for no restriction
maxResponseSize: 3 * 1024 * 1024, // 3 MB
headers: {
// override default security headers
'X-Custom-Header': 'HelloWorld'
}
});
@Post()
async createUser(@Req() req: Request, @Res() res: Response, @Next() next: NextFunction) {
// 4) Just call the built handler (Express-compatible)
return await this.createUserHandler(req, res, next);
}
}
Explanation
bodySchema
–ajv
uses this to validate the request body. If validation fails, an error withvalidationErrors
is returned.initialQueryReflector
– This uses@denis_bruns/reflection
to parse the relevant fields out ofreq.body
(or path/query parameters).handlers
– The inline function(s) that will be executed in order, each returning anObservable
orPromise
.timeoutMs
&maxResponseSize
– Protect your service from slow or large payloads.- CORS – Provide a
corsOriginWhitelist
array to allow specific origins or default to*
.
Custom Error Mappings
If your “use case” throws custom errors, you can map them to specific status codes:
class MyCustomError extends Error {}
const myUseCase: IUseCaseInlineFunc<UserDTO, UserDTO, CreatedUser> = (query) => ({
execute: () => {
if (!query.data?.email?.endsWith('@allowed.com')) {
throw new MyCustomError('Only allowed.com domain is permitted.');
}
return Promise.resolve({ id: '777', name: query.data.name! });
}
});
const handler = nestJsRouteHandlerBuilder<UserDTO, [typeof myUseCase]>({
initialQueryReflector: {/* ... */},
handlers: [myUseCase],
errorToStatusCodeMapping: {
418: [MyCustomError], // Return 418 for MyCustomError
}
});
Related Packages
@denis_bruns/core
Core types likeIUseCaseInlineFunc
,IQueryType
,IJsonSchema
, and essential error classes.@denis_bruns/reflection
Used for the “reflector” mechanism to extract/transform request data via JSONPath or functions.
Contributing
Questions, issues, or improvements? Feel free to open a pull request or file an issue on GitHub.
License
This project is MIT licensed.