引言:为什么TypeScript和REST API是快速开发的理想组合
\n现代Web开发需要速度和可靠性。如果你需要快速为移动应用、Web服务或微服务架构构建后端,TypeScript + Node.js + Express 组合是最有效的工具之一。TypeScript为JavaScript增加了严格的类型检查,让你能在编译阶段而非运行时捕获错误。这在构建REST API时尤其有价值,因为每个端点都需要清楚知道它接收和返回什么数据。
\n在本文中,我们将在30分钟内创建一个用于管理任务列表(To-Do List)的完整REST API。你将学习如何配置项目、编写控制器、路由和中间件。所有内容都基于包含完整代码的实战示例。
\n\n项目准备与配置(5分钟)
\n首先,创建项目结构并安装依赖。
\nmkdir todo-api\ncd todo-api\nnpm init -y\nnpm install express cors uuid\nnpm install -D typescript @types/express @types/cors @types/uuid ts-node nodemon\n在项目根目录创建 tsconfig.json 文件:
{\n "compilerOptions": {\n "target": "ES2020",\n "module": "commonjs",\n "outDir": "./dist",\n "rootDir": "./src",\n "strict": true,\n "esModuleInterop": true,\n "skipLibCheck": true\n },\n "include": ["src/**/*"]\n}\n在 package.json 中添加脚本:
"scripts": {\n "dev": "nodemon --exec ts-node src/index.ts",\n "build": "tsc",\n "start": "node dist/index.js"\n}\n创建 src 文件夹和入口文件 index.ts。
基本文件夹结构
\n- \n
src/index.ts— 入口文件,启动服务器 \n src/routes/tasks.ts— 任务路由 \n src/controllers/taskController.ts— 请求处理逻辑 \n src/models/task.ts— 接口和类型定义 \n src/middleware/errorHandler.ts— 错误处理(可选) \n
创建数据模型和接口(5分钟)
\n为任务定义数据类型。创建文件 src/models/task.ts:
export interface Task {\n id: string;\n title: string;\n completed: boolean;\n createdAt: Date;\n}\n\nexport type CreateTaskDto = Pick<Task, 'title'>;\nexport type UpdateTaskDto = Partial<Pick<Task, 'title' | 'completed'>>;\n这里我们使用了 Pick 和 Partial —— TypeScript 内置的工具类型。CreateTaskDto 只接受标题,而 UpdateTaskDto 包含用于部分更新的可选字段。
编写控制器和路由(15分钟)
\n现在进入最有趣的部分——应用逻辑。我们将创建一个内存中的临时任务存储。生产环境中应替换为数据库。
\n\n任务控制器
\n文件 src/controllers/taskController.ts:
import { Request, Response } from 'express';\nimport { v4 as uuidv4 } from 'uuid';\nimport { Task, CreateTaskDto, UpdateTaskDto } from '../models/task';\n\n// 临时存储\nlet tasks: Task[] = [];\n\n// GET /api/tasks\nexport const getAllTasks = (req: Request, res: Response) => {\n res.json(tasks);\n};\n\n// GET /api/tasks/:id\nexport const getTaskById = (req: Request, res: Response) => {\n const task = tasks.find(t => t.id === req.params.id);\n if (!task) {\n return res.status(404).json({ message: '任务未找到' });\n }\n res.json(task);\n};\n\n// POST /api/tasks\nexport const createTask = (req: Request, res: Response) => {\n const { title } = req.body as CreateTaskDto;\n if (!title || typeof title !== 'string') {\n return res.status(400).json({ message: 'title 字段为必填项,且必须为字符串' });\n }\n