Skip to main content

Quickstart

Installation

Install the core package

npm install @ts-rest/core

Create a contract

npm install zod

Create a contract, this should ideally be shared between your consumers and producers, e.g. in a shared library in a monorepo, or a shared npm package.

In this case we're using Zod, this has some nice features, like enabling body parsing and OpenAPI type generation.

// contract.ts

import { initContract } from '@ts-rest/core';
import { z } from 'zod';

const c = initContract();

const PostSchema = z.object({
id: z.string(),
title: z.string(),
body: z.string(),
});

export const contract = c.router({
createPost: {
method: 'POST',
path: '/posts',
responses: {
201: PostSchema,
},
body: z.object({
title: z.string(),
body: z.string(),
}),
summary: 'Create a post',
},
getPost: {
method: 'GET',
path: `/posts/:id`,
responses: {
200: PostSchema.nullable(),
},
summary: 'Get a post by id',
},
});

Server Implementation

npm install @ts-rest/nest

ts-rest offers a unique way to create a fully type safe REST API server, normally Nest APIs are extremely powerful, but hard to make type safe.

// post.controller.ts

const c = nestControllerContract(apiBlog);
type RequestShapes = NestRequestShapes<typeof c>;

@Controller()
export class PostController implements NestControllerInterface<typeof c> {
constructor(private readonly postService: PostService) {}

@TsRest(c.getPost)
async getPost(@TsRestRequest() { params: { id } }: RequestShapes['getPost']) {
const post = await this.postService.getPost(id);

return { status: 200 as const, body: post };
}

@TsRest(c.createPost)
async createPost(@TsRestRequest() { body }: RequestShapes['createPost']) {
const post = await this.postService.createPost({
title: body.title,
body: body.body,
});

return { status: 201 as const, body: post };
}
}

Client Implementation

This is the basic client, using fetch under the hood which is exported from @ts-rest/core.

// client.ts

const client = initClient(contract, {
baseUrl: 'http://localhost:3000',
baseHeaders: {},
});

const { body, status } = await client.createPost({
body: {
title: 'Post Title',
body: 'Post Body',
},
});

if (status === 201) {
// body is Post
console.log(body);
} else {
// body is unknown
console.log(body);
}