DEV Community

Alex Spinov
Alex Spinov

Posted on

ts-rest Has a Free API That Most Developers Dont Know About

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() })) } }
});
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

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" } });
Enter fullscreen mode Exit fullscreen mode

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)