Incrementally adoptable RPC-like client and server helpers for a magical end to end typed experience πͺ
Introduction
ts-rest offers a simple way to define a contract for your API, which can be both consumed and implemented by your application, giving you end to end type safety without the hassle or code generation.
Features
- End-to-end type safety π
- RPC-like client side API β‘οΈ
- Small Bundle Size π
- No Code Generation πββοΈ
- Zod support for runtime validation π
- Full optional OpenAPI integration π
Super Simple Example
Easily define your API contract somewhere shared
const contract = c.router({ getPosts: { method: 'GET', path: '/posts', query: z.object({ skip: z.number(), take: z.number(), }), // <-- Zod schema responses: { 200: c.type<Post[]>(), // <-- OR normal TS types }, headers: z.object({ 'x-pagination-page': z.coerce.number().optional(), }), }, });
Fulfill the contract on your server, with a type-safe router:
const router = s.router(contract, { getPosts: async ({ params: { id } }) => { return { status: 200, body: prisma.post.findUnique({ where: { id } }), }; }, });
Consume the api on the client with a RPC-like interface:
const result = await client.getPosts({ headers: { 'x-pagination-page': 1 }, query: { skip: 0, take: 10 }, // ^-- Fully typed! });
Quickstart
Create a contract, implement it on your server then consume it in your client. Incrementally adopt, trial it with your team, then get shipping faster.
Contributors β¨
MASSIVE Thanks to all of these wonderful people (emoji key), who have helped make ts-rest possible:
Star History
Since our first commit in 2022 we've been growing steadily. We're proud of our progress and we're excited about the future.