在TypeScript中GraphQL与REST:巨头对决还是按需选择?
\n\n当你使用TypeScript进行开发时,后端架构问题变得尤为突出。你不仅希望“能运行”,更希望类型安全、编译阶段就能捕获错误,并且代码可预测。此时,开发者面临一个永恒的选择:久经考验的REST,还是时尚灵活的GraphQL?这两种技术都能与TypeScript完美配合,但各自决定了不同的开发风格。
\n\n在本文中,我们不会泛泛而谈或讲述基本常识。我们将通过具体示例、代码、瓶颈以及一种方案碾压另一种方案的场景,深入剖析TypeScript中的GraphQL与REST。开始吧!
\n\nREST:值得信赖的老朋友
\n\nREST(表述性状态转移)是一种架构风格,其中每个资源(用户、帖子、评论)都有其自己的URL。你访问/api/users/1就能获取用户信息。简单且可预测。在TypeScript项目中,REST通常通过Express、Fastify或NestJS实现。
TypeScript中的REST类型化
\nREST的主要痛点在于后端与前端之间缺乏强关联。你发送JSON数据,前端却要猜测:“返回了什么?”。TypeScript通过共享类型或OpenAPI(Swagger)解决了这个问题。
\n\n示例:基于Express和TypeScript的后端
\n\nimport express, { Request, Response } from 'express';\n\ninterface User {\n id: number;\n name: string;\n email: string;\n}\n\nconst app = express();\napp.get('/api/users/:id', (req: Request, res: Response) => {\n const user: User = { id: +req.params.id, name: 'Alice', email: 'alice@example.com' };\n res.json(user);\n});\n\n\n前端(fetch):
\n\ninterface User {\n id: number;\n name: string;\n email: string;\n}\n\nasync function getUser(id: number): Promise<User> {\n const res = await fetch(`/api/users/${id}`);\n return res.json() as Promise<User>;\n}\n\n\n问题:如果后端修改了User结构,前端只能在运行时才能发现。前端的类型仅仅是副本。解决方案:从OpenAPI规范生成类型。
\n\nTypeScript中REST的优点
\n- \n
- 缓存简单:HTTP缓存(CDN、浏览器)开箱即用。 \n
- 结构清晰:URL = 资源,方法 = 操作(GET、POST、PUT、DELETE)。 \n
- 广泛支持:任何客户端(浏览器、curl、Postman)都能理解REST。 \n
- 入门门槛低:即使是新手也能在一小时内掌握。 \n
REST的缺点
\n- \n
- 过度获取和欠获取:你要么获取了多余字段,要么需要发起多次请求。 \n
- 结构僵化:要获取用户及其帖子,需要两次请求或使用变通方法(expand字段)。 \n
- 类型化较弱:没有OpenAPI时,客户端的类型只是“口头承诺”。 \n
GraphQL:灵活性与控制力
\n\nGraphQL是一种查询语言,允许客户端自行决定获取哪些数据。一个端点(/graphql),一次查询——你就能精确获得所需内容。TypeScript和GraphQL是理想组合:类型可以从模式中自动生成。
TypeScript中的GraphQL类型化
\n首先编写模式(SDL):
\n\ntype User {\n id: ID!\n name: String!\n email: String!\n posts: [Post!]!\n}\n\ntype Post {\n id: ID!\n title: String!\n content: String!\n}\n\ntype Query {\n user(id: ID!): User\n}\n\n\n然后生成类型(通过graphql-codegen):
// generated.ts\nexport type User = {\n __typename?: 'User';\n id: string;\n name: string;\n email: string;\n posts: Array<Post>;\n};\n\nexport type Post = {\n __typename?: 'Post';\n id: string;\n title: string;\n content: string;\n};\n