ts-rest 快速入门指南:构建类型安全的 REST API 系统
前言
在现代 Web 开发中,前后端分离架构已成为主流。然而,随着项目规模扩大,前后端接口的维护和同步往往成为痛点。ts-rest 项目应运而生,它提供了一种类型安全的方式来定义和实现 REST API,确保前后端在接口层面始终保持一致。
核心概念
ts-rest 的核心思想是通过 TypeScript 类型系统来定义 API 契约(Contract),这个契约可以在前后端共享,从而获得以下优势:
- 类型安全:编译时就能捕获接口不匹配的问题
- 自动补全:IDE 能提供准确的参数提示
- 减少文档:代码即文档,减少维护文档的工作量
- 统一验证:前后端使用相同的验证逻辑
环境准备
安装核心包
首先需要安装 ts-rest 的核心包:
npm install @ts-rest/core
配置 TypeScript
确保你的 tsconfig.json
中启用了严格模式:
{
"compilerOptions": {
"strict": true
}
}
严格模式是 ts-rest 正常工作的重要前提,它能提供更完善的类型检查。
定义 API 契约
API 契约是 ts-rest 的核心,它定义了接口的路径、方法、请求参数和响应格式。建议将契约定义在共享的位置,前后端都能访问。
使用 Zod 定义契约(推荐)
import { initContract } from '@ts-rest/core';
import { z } from 'zod';
const c = initContract();
const Pokemon = z.object({
name: z.string(),
});
export const pokemonContract = c.router({
getPokemon: {
method: 'GET',
path: '/pokemon/:id',
responses: {
200: Pokemon,
},
summary: '根据ID获取宝可梦信息',
},
});
使用 Zod 等验证库的好处是:
- 提供运行时验证
- 自动生成类型定义
- 支持复杂的验证规则
其他验证库选项
ts-rest 也支持 Valibot、Arktype 等验证库,或者不使用任何验证库(仅类型)。选择哪种方式取决于项目需求和技术栈。
服务端实现
ts-rest 支持多种 Node.js 框架,下面以 Express 为例:
Express 实现
import express from 'express';
import { initServer } from '@ts-rest/express';
import { pokemonContract } from './contract';
const app = express();
const s = initServer();
const router = s.router(pokemonContract, {
getPokemon: async ({ params: { id } }) => {
const pokemon = { name: '皮卡丘' };
if (id !== '1') {
return {
status: 404,
body: null,
};
}
return {
status: 200,
body: pokemon,
};
},
});
app.use(express.json());
app.use('/api', router);
app.listen(3000, () => {
console.log('服务器已启动: http://localhost:3000');
});
其他框架支持
- NestJS:提供装饰器风格的实现
- Fastify:高性能框架实现
- Next.js:适合全栈应用
每种实现都保持了相同的类型安全特性,开发者可以根据项目需求选择合适的框架。
客户端实现
基础 Fetch 客户端
import { initClient } from '@ts-rest/core';
import { pokemonContract } from './contract';
const client = initClient(pokemonContract, {
baseUrl: 'http://localhost:3000',
});
const { status, body } = await client.getPokemon({
params: { id: '1' },
});
if (status === 200) {
console.log(`宝可梦名称: ${body.name}`);
} else {
console.log('未找到宝可梦');
}
React Query 集成
对于前端应用,特别是 React 项目,推荐使用 React Query 集成:
import { initQueryClient } from '@ts-rest/react-query';
const client = initQueryClient(pokemonContract, {
baseUrl: 'http://localhost:3000',
});
function PokemonComponent() {
const { data, isLoading } = client.getPokemon.useQuery(
['pokemon', '1'],
{ params: { id: '1' } }
);
if (isLoading) return <div>加载中...</div>;
if (data?.status !== 200) return <div>获取数据失败</div>;
return <div>{data.body.name}</div>;
}
React Query 集成提供了:
- 自动缓存
- 请求状态管理
- 自动重试
- 乐观更新等高级功能
最佳实践
- 共享契约:将契约定义在前后端都能访问的共享位置
- 验证库:优先使用 Zod 等验证库,确保运行时安全
- 错误处理:统一前后端的错误响应格式
- 文档生成:考虑从契约自动生成 API 文档
- 版本控制:为契约添加版本信息,便于后续演进
总结
ts-rest 为 TypeScript 项目提供了一套完整的类型安全 REST API 解决方案。通过定义共享契约,开发者可以:
- 减少前后端沟通成本
- 提高开发效率(自动补全、类型检查)
- 降低运行时错误
- 简化测试和维护
无论是小型项目还是大型企业应用,ts-rest 都能显著提升 API 开发的体验和质量。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考