Type Safe, contract-driven REST Controllers for NestJS applications.
Uses zod object defintions and ts-rest to create typesafe REST endpoint contracts.
Note This library is simply to force NestJS controllers to implement a ts-rest contract. The contracts and client lib themselves are still standard ts-rest.
- Type-Safe Controllers - Controllers are forced to implement the interface defined by the contract
- Automatic request body, path param, and query parsing using the
zod
definitions defined in the contract. - Automatic parameter unmarshelling - no need for additional param decorators like
Body
orApiDecorator
. Its already handled. - Automatic binding of methods to endpoints - no need for additional decorators like
Post
orApi
. Its already handled. - Utilities for creating response objects like
NoContent
,Ok
etc that provide the correct status codes. - Combine contracts as per
ts-rest
.
npm i @ts-rest/core nestjs-rest-contracts
- [] Ensure all entire contract has been implemented
- []
ContractModule
- could be used for things like automatically creating a swagger doc + route if enabled. E.g.
@Module({
imports: [ContractModule.forRoot({
openApi: {
enabled: true
}
})]
})
export class AppModule {
}
- Create contract
import { initContract } from '@ts-rest/core';
import { z } from 'zod';
export const c = initContract();
export const stackContract = c.router({
update: {
method: 'POST',
path: '/stacks/:name/update',
responses: {
204: c.response<void>(),
409: c.response<{ message: string }>(),
},
body: z.object({
foo: z.string(),
}),
pathParams: {
name: z.string(),
},
summary: 'Run update for stack',
},
});
- Create your Controller
import { stackContract } from './contract';
import {
ContractController,
ArgsShape,
ResponseShape,
NoContent,
Conflict,
} from 'nestjs-rest-contracts';
type Contract = typeof stackContract;
type Args<T extends keyof Contract> = ArgsShape<Contract[T]>;
type Response<T extends keyof Contract> = ResponseShape<Contract[T]>;
@ContractController(stackContract)
export class StackController {
async update(args: Args<'update'>): Response<'update'> {
if (args.params.name === 'foo') {
return NoContent();
}
return Conflict('some message');
}
}
- Register your Controller
import { Module } from '@nestjs/common';
import { StackController } from './stack.controller';
@Module({
controllers: [StackController],
})
export class AppModule {}
- Profit