ts-rest creates type-safe REST APIs with a shared contract between client and server. Like tRPC but for REST endpoints.
Define Contract
import { initContract } from "@ts-rest/core";
import { z } from "zod";
const c = initContract();
export const contract = c.router({
getPost: { method: "GET", path: "/posts/:id", responses: { 200: z.object({ id: z.string(), title: z.string() }) } },
createPost: { method: "POST", path: "/posts", body: z.object({ title: z.string() }), responses: { 201: z.object({ id: z.string() }) } },
listPosts: { method: "GET", path: "/posts", query: z.object({ page: z.number().optional() }), responses: { 200: z.array(z.object({ id: z.string(), title: z.string() })) } }
});
Server (Express/Next.js)
import { createExpressEndpoints } from "@ts-rest/express";
const router = createExpressEndpoints(contract, {
getPost: async ({ params }) => ({ status: 200, body: await db.getPost(params.id) }),
createPost: async ({ body }) => ({ status: 201, body: await db.createPost(body) }),
listPosts: async ({ query }) => ({ status: 200, body: await db.listPosts(query.page) })
}, app);
Client
import { initClient } from "@ts-rest/core";
const client = initClient(contract, { baseUrl: "http://localhost:3000" });
const post = await client.getPost({ params: { id: "123" } }); // Fully typed!
const newPost = await client.createPost({ body: { title: "Hello" } });
Key Features
- Shared contract for type safety
- REST-native (not RPC)
- Works with Express, Next.js, Fastify
- React Query integration
- OpenAPI generation
Need to scrape or monitor web data at scale? Check out my web scraping actors on Apify or email spinov001@gmail.com for custom solutions.
Top comments (0)