by koto

ts-rest provides RPC-like client and server helpers that share a single API contract, delivering full end-to-end type safety without code generation. Built for TypeScript teams using Express, Fastify, NestJS, Next.js, or serverless platforms.
This block provides the ts-rest suite of libraries for building end-to-end type-safe REST APIs in TypeScript. It ships the core contract DSL plus server adapters for Express, Fastify, NestJS, and Next.js. The typical buyer is a TypeScript team that wants RPC-like type safety across client and server without code generation.
core/ - Contract DSL, type utilities, client factory, Zod/Standard Schema integration, and shared error typesexpress/ - Express router adapter: createExpressEndpoints, request validation, typed handlersfastify/ - Fastify plugin adapter for ts-rest contractsnest/ - NestJS decorators, interceptors, module, and handler utilitiesnext/ - Next.js API route adapter and client helpersopen-api/ - OpenAPI document generation from ts-rest contractsreact-query/ - TanStack Query v4 integration for contract clientsreact-query-v5/ - TanStack Query v5 integrationserverless/ - AWS Lambda / Azure Functions adaptersolid-query/ - Solid.js TanStack Query integrationvue-query/ - Vue TanStack Query integrationnpm install zod
npm install @ts-rest/core
npm install @ts-rest/express # if using Express
npm install @ts-rest/fastify # if using Fastify
npm install @ts-rest/nest # if using NestJS
npm install @ts-rest/next # if using Next.js
npm install @ts-rest/react-query # if using React + TanStack Query v4
npm install @ts-rest/open-api # if generating OpenAPI docs
Express peer dependencies:
npm install express
npm install @types/express --save-dev
NestJS peer dependencies:
npm install @nestjs/common @nestjs/core @nestjs/platform-express
Copy the source/ directory into your repository, e.g. libs/ts-rest/.
Add path aliases in tsconfig.json so local packages resolve correctly:
{
"compilerOptions": {
"paths": {
"@ts-rest/core": ["libs/ts-rest/core/src/index.ts"],
"@ts-rest/express": ["libs/ts-rest/express/src/index.ts"],
"@ts-rest/nest": ["libs/ts-rest/nest/src/index.ts"],
"@ts-rest/next": ["libs/ts-rest/next/src/index.ts"],
"@ts-rest/open-api": ["libs/ts-rest/open-api/src/index.ts"],
"@ts-rest/react-query": ["libs/ts-rest/react-query/src/index.ts"]
},
"strict": true
}
}
Spin up an isolated sandbox and run it server-side — no local setup.
Tetrees AI Review of this version
This Express backend / api completed archive review. Structure, dependency manifests, documentation, functional source, and common risk patterns were checked by the Tetrees verification pipeline; runtime phases are stated separately.
Deterministic AVCP artifact review
Pipeline avcp-2026-08-04.1 · SHA-256 ce3453e2780b78a9…
This version-scoped review deterministically inspects the submitted archive for structure, dependencies, documentation, functional source, and common malicious or high-risk signals. Build and test phases are reported as passed only after an isolated sandbox audition. It is not a guarantee of perfect security.
Reviewed Aug 4, 2026
Push this product straight into your AI IDE, web builder or cloud IDE.
Connect Tetrees to a compatible AI IDE, list products you own, and request the verified ZIP without exposing seller upload controls.
No reviews yet.
Sign in to join the discussion
Loading discussion…
If using Jest, add moduleNameMapper entries mirroring the paths above in jest.config.ts.
No environment variables are required by the core library. Individual adapters (serverless, etc.) may require cloud-provider credentials at runtime — consult their README files inside source/.
Ensure "moduleResolution": "bundler" or "node16" in tsconfig.json for ESM compatibility.
initContractimport { initContract } from '@ts-rest/core';
const c = initContract();
Entry point for defining an API contract. Call c.router(...) to declare route shapes with method, path, query/body/response schemas. The returned contract object is the single source of truth shared between client and server.
initClientimport { initClient } from '@ts-rest/core';
const client = initClient(contract, {
baseUrl: 'http://localhost:3000',
baseHeaders: {},
});
Creates a fully typed RPC-like client from a contract. Each key on client is an async function whose parameters and return type are inferred from the contract definition. Use this on the frontend or in tests to call the API.
createExpressEndpointsimport { createExpressEndpoints } from '@ts-rest/express';
createExpressEndpoints(contract, router, app, options?);
Mounts all routes from a ts-rest server router onto an Express app, wiring up request validation, response shaping, and type checking automatically. Call this once during server startup after defining your implementation router.
RequestValidationErrorimport { RequestValidationError } from '@ts-rest/express';
// shape: { pathParams, headers, query, body } — each is a ZodError or undefined
Thrown (or passed to next()) when an incoming Express request fails contract validation. Catch it in an Express error handler to return structured 400 responses.
TsRestModule (NestJS)import { TsRestModule } from '@ts-rest/nest';
@Module({ imports: [TsRestModule.register({ isGlobal: true })] })
export class AppModule {}
NestJS dynamic module that wires the ts-rest interceptor globally. Register it once in your root module; individual controllers then use @TsRest(contract) decorators.
Define the contract in a shared file, then consume it with initClient:
import { initContract } from '@ts-rest/core';
import { initClient } from '@ts-rest/core';
import { z } from 'zod';
const c = initContract();
export const contract = c.router({
getUser: {
method: 'GET',
path: '/users/:id',
pathParams: z.object({ id: z.string() }),
responses: { 200: z.object({ id: z.string(), name: z.string() }) },
},
createUser: {
method: 'POST',
path: '/users',
body: z.object({ name: z.string() }),
responses: { 201: z.object({ id: z.string(), name: z.string() }) },
},
});
const client = initClient(contract, {
baseUrl: 'http://localhost:3000',
baseHeaders: {},
});
async function main() {
const result = await client.getUser({ params: { id: '42' } });
if (result.status === 200) {
console.log(result.body.name);
}
}
import express from 'express';
import { initServer } from '@ts-rest/core';
import { createExpressEndpoints, RequestValidationError } from '@ts-rest/express';
import { contract } from './contract';
const app = express();
app.use(express.json());
const s = initServer();
const router = s.router(contract, {
getUser: async ({ params }) => {
// params.id is typed as string
return { status: 200, body: { id: params.id, name: 'Alice' } };
},
createUser: async ({ body }) => {
return { status: 201, body: { id: 'new-id', name: body.name } };
},
});
createExpressEndpoints(contract, router, app);
// Handle validation errors
app.use((err: unknown, _req: any, res: any, next: any) => {
if (err instanceof RequestValidationError) {
return res.status(400).json({ error: 'Validation failed', details: err.body?.issues });
}
next(err);
});
app.listen(3000);
import { Controller } from '@nestjs/common';
import { TsRest, TsRestHandler, tsRestHandler } from '@ts-rest/nest';
import { contract } from './contract';
@Controller()
export class UserController {
@TsRest(contract.getUser)
@TsRestHandler(contract.getUser)
async getUser() {
return tsRestHandler(contract.getUser, async ({ params }) => {
return { status: 200 as const, body: { id: params.id, name: 'Alice' } };
});
}
}
Register TsRestModule in AppModule and the interceptor handles response shaping automatically.
core/ - The foundation: initContract, initClient, initServer, Zod utilities, path helpers, type inference utilities, and error classes (ResponseValidationError, UnknownStatusError, ValidationError).express/ - Express adapter. createExpressEndpoints is the primary export; RequestValidationError surfaces validation failures; types.ts holds handler/options type definitions.fastify/ - Fastify plugin adapter. Exposes a single entry via ts-rest-fastify.ts.nest/ - Full NestJS integration: TsRestModule, @TsRest() route decorator, @TsRestRequest() param decorator, interceptor for response shaping, and tsRestHandler helper.next/ - Next.js API route helpers (createNextRouter) and a Next.js-compatible client factory.open-api/ - Generates an OpenAPI 3.x document from any ts-rest contract; no runtime dependency on Express or Fastify.react-query/ - Wraps initClient to return TanStack Query v4 hooks (useQuery, useMutation) typed from the contract.react-query-v5/ - Same as above but for TanStack Query v5 API.serverless/ - Adapters for AWS Lambda and Azure Functions; maps handler inputs/outputs to the ts-rest contract shape.solid-query/ - Solid.js TanStack Query hooks, mirroring the react-query adapter.vue-query/ - Vue TanStack Query hooks, mirroring the react-query adapter.tsconfig paths are compile-time only; add the same mappings to tsconfig-paths or use a bundler that resolves them (Webpack, Vite, esbuild with alias).initServer missing from imports: It is exported from @ts-rest/core (re-exported via server.ts), not from the adapter packages; import it from @ts-rest/core.z.object inference — pin "zod": "^3.22.0" in package.json.status must be const-asserted: Return { status: 200 as const, body: ... } in server handlers; without as const, TypeScript widens to number and the discriminated union breaks.ValidationPipe globally alongside ts-rest can double-validate bodies and throw before ts-rest sees the request; disable ValidationPipe for ts-rest routes or set validateCustomDecorators: false."module": "CommonJS" in the adapter's tsconfig.lib.json or configure dual-package exports in package.json.I have the ts-rest source libraries located at `libs/ts-rest/` in my project.
I also have `USAGE.md` in the same directory as this prompt.
My project uses: [describe your stack: Express / NestJS / Next.js, Zod version, TypeScript version, monorepo tool if any].
Please help me integrate ts-rest step by step:
1. Read `USAGE.md` and the file excerpts to understand the real exports.
2. Create a shared contract file using `initContract` from `@ts-rest/core`.
3. Wire up the server adapter appropriate for my framework using the real
exported functions (`createExpressEndpoints`, `TsRestModule`, etc.).
4. Create a typed client using `initClient` from `@ts-rest/core`.
5. Add tsconfig path aliases pointing to `libs/ts-rest/*/src/index.ts`.
6. Show me how to handle `RequestValidationError` (Express) or use
`TsRestHandler` (NestJS) with real import paths from `libs/ts-rest/`.
Do not invent any exports. Only use symbols documented in `USAGE.md`.
ts-rest is released under the MIT License (see source/LICENSE if present, or the GitHub repository). Upstream package: @ts-rest/core and related @ts-rest/* packages maintained by the ts-rest organization.
This product’s unique contribution is clean room implementation of valuable code block into AVCP compatible standard.
The full install guide and integration prompts unlock after purchase.
Automation, Utilities & Developer Tools
Free