Swagger
@ApiSafeResponse(Model)
Documents the Swagger data field with a specific DTO type.
@Get(':id')
@ApiSafeResponse(UserDto)
async findOne(@Param('id') id: string) {
return this.usersService.findOne(id);
}Options: isArray, statusCode, description
Composite Decorators
Combine Swagger documentation, runtime behavior, and error responses into a single decorator.
@SafeEndpoint(Model, options?)
@Get()
@SafeEndpoint(UserDto, {
description: 'List users',
errors: [401, { status: 404, code: 'USER_NOT_FOUND' }],
message: 'Users fetched',
})
findAll() { ... }Equivalent to stacking @ApiSafeResponse() + @ResponseMessage() + @ApiSafeErrorResponses().
Options: statusCode, isArray, description, sort, filter, message, code, errors, deprecated, problemDetails
@SafePaginatedEndpoint(Model, options?)
@Get()
@SafePaginatedEndpoint(UserDto, {
maxLimit: 100,
links: true,
errors: [401],
})
findAll() { ... }Equivalent to @ApiPaginatedSafeResponse() + @Paginated() + @ApiSafeErrorResponses().
Options: maxLimit, links, sort, filter, description, message, code, errors, deprecated, problemDetails
@SafeCursorPaginatedEndpoint(Model, options?)
@Get()
@SafeCursorPaginatedEndpoint(UserDto, {
maxLimit: 50,
errors: [401],
})
findAll() { ... }Equivalent to @ApiCursorPaginatedSafeResponse() + @CursorPaginated() + @ApiSafeErrorResponses().
Options: maxLimit, links, sort, filter, description, message, code, errors, deprecated, problemDetails
problemDetailsoption: Whentrue, error responses useapplication/problem+jsonschema in Swagger. Must match the module-levelproblemDetailssetting — this option only controls Swagger documentation, not runtime behavior.
@SkipGlobalErrors()
Excludes a route from applyGlobalErrors() global error injection. Useful for routes with custom error schemas.
@Exclude() Integration
Using with class-transformer
@nestarc/safe-response works with NestJS's ClassSerializerInterceptor when registered in the correct order. SafeResponseModule must be imported before ClassSerializerInterceptor is registered, so that serialization runs first and response wrapping runs second.
import { ClassSerializerInterceptor, Module } from '@nestjs/common';
import { APP_INTERCEPTOR } from '@nestjs/core';
@Module({
imports: [SafeResponseModule.register()],
providers: [
{ provide: APP_INTERCEPTOR, useClass: ClassSerializerInterceptor },
],
})
export class AppModule {}Frontend Client Types
@nestarc/safe-response/client provides zero-dependency TypeScript types and type guards for frontend consumers. No NestJS, Swagger, or reflect-metadata required.
import type { SafeAnyResponse } from '@nestarc/safe-response/client';
import {
isSuccess, isError, isPaginated, isOffsetPagination, isCursorPagination,
isProblemDetailsResponse, hasResponseTime, hasSort, hasFilters,
isDeprecated, hasRateLimit,
} from '@nestarc/safe-response/client';
// SafeAnyResponse includes success, error, and Problem Details responses
const res: SafeAnyResponse<User[]> = await fetch('/api/users').then(r => r.json());
if (isSuccess(res)) {
console.log(res.data); // User[]
if (isPaginated(res.meta) && isOffsetPagination(res.meta.pagination)) {
console.log(`Page ${res.meta.pagination.page} of ${res.meta.pagination.totalPages}`);
}
}
if (isError(res)) {
console.error(res.error.code, res.error.message);
}
// RFC 9457 Problem Details (different shape from standard errors)
if (isProblemDetailsResponse(res)) {
console.error(res.type, res.detail, res.instance);
}