這款TypeScript庫(kù)讓我快速搭建全棧應(yīng)用變得更簡(jiǎn)單了
以前当我构建web应用时,我通常会以相同的方式做事。我搭建了一个基于Node.js和Express的后端,并创建了标准的REST API接口。虽然有效,但感觉一直在重复同样的工作。
对于每个新特性,我都要进行...
- 在服务器上定义API端点。
- 确定该端点需要的数据类型以及它将返回的数据类型。
- 前往前端代码(通常是React),编写调用该特定API端点的代码。
- 在前端再次定义类型,以便它知道期望接收什么样的数据。
- 通常,我需要使用额外的工具,如Redux,来管理数据获取、显示加载指示器以及在React中处理错误。
这意味着关于我的 API(路径和数据类型)的信息在两个地方是一样的。如果我在后端做了改动,比如让 API 返回不同的数据,我得记得更新前端代码。如果我忘记了更新,系统就会出问题。作为一个单打独斗的团队,试图快速构建东西时,保持所有东西匹配起来既慢又有点烦人。
我是一名独立开发者,正在开发 UserJot,并且需要一种更快的方式来确保一切顺利,同时保持代码整洁。找到这样的方法对我来说非常重要。
寻找更简单的途径:tRPC
然后,在2023年,我遇到了一个名为tRPC的TypeScript库,它听起来真的很酷。它的主要理念听起来真的很酷:如果前端代码可以直接调用一个普通函数一样的后端代码,所有的数据类型能够自动匹配起来会怎么样?我不确定这是否只是炒作,但它听起来足够有趣,值得一试。我在一个小项目上花了一周左右的时间来尝试它。
说实话,这对我来说简单多了,也更快了。
它是如何工作的:快速了解
使用 tRPC,你对 REST 端点的想法会有所不同。相反,你可以在后端创建“函数”。你可以把它们想象成前端可以调用的函数。你可以使用像 Zod 这样的辅助库来明确定义这些函数输入和输出的数据类型。
下面是一个简单的想法:
后端(服务器代码:)
// server.ts
import { initTRPC } from '@trpc/server';
import { z } from 'zod'; // Zod 用于定义数据类型
const t = initTRPC.create();
// 定义前端可以调用的接口
export const appRouter = t.router({
// 获取用户数据的接口
getUser: t.procedure
.input(z.object({ userId: z.string() })) // 需要一个字符串的 userId
.query(({ input }) => {
// 查找用户逻辑...
return { id: input.userId, name: 'Example User' }; // 返回用户对象
}),
// 创建新用户接口
createUser: t.procedure
.input(z.object({ name: z.string() })) // 需要一个字符串的 name
.mutation(({ input }) => {
// 创建用户逻辑...
return { id: 'newUser123', name: input.name }; // 返回新用户
}),
});
// 导出路由器的类型定义
export type AppRouter = typeof appRouter;
进入全屏 退出全屏
在这里我们定义了两个函数:getUser
和 createUser
。Zod 校验输入数据。重要的是导出 AppRouter
接口——这定义了 TypeScript 中我们的 API 的样子。
前端 (用 React 编写的客户端代码):
tRPC 和前端工具如 React Query 配合得非常棒。你只需在前端设置 tRPC 客户端,并指定从后端导出的 AppRouter
类型。
// client.tsx (React 示例)
import { createTRPCReact } from '@trpc/react-query';
import type { AppRouter } from './server'; // 从后端导入类型
const trpc = createTRPCReact<AppRouter>();
function UserProfile() {
// 这个钩子知道 'getUser' 需要什么数据并返回什么!
const userQuery = trpc.getUser.useQuery({ userId: '123' });
if (userQuery.isLoading) return <div>加载中...</div>;
if (userQuery.error) return <div>错误: {userQuery.error.message}</div>;
// 我们知道 `userQuery.data` 包含一个 `name` 属性,因为 tRPC!
return <div>用户名: {userQuery.data.name}</div>;
}
function CreateUserButton() {
const mutation = trpc.createUser.useMutation();
const handleCreate = () => {
// tRPC 会检查我们发送的输入是否符合要求 (`{ name: string }`)
mutation.mutate({ name: '新用户' });
};
return <button onClick={handleCreate} disabled={mutation.isLoading}>创建用户</button>;
}
点击全屏 点击退出全屏
看看前端是如何导入 AppRouter
类型的?注意这里的导入方式,正因为这样导入,trpc.getUser.useQuery
钩子会自动识别它需要一个 userId
,并且它的 data
包含 id
和 name
。你再也不用在客户端手动写 fetch 请求或 API 数据的类型定义了。它就这么知道啦!
对我而言真正好转的是
使用 tRPC 这个工具解决了我最主要的头疼问题,
- 更快的构建: 我不再重复编写相同的API信息。在后端创建一个函数后,它会在前端立即准备好并完成类型定义。这使得我能够更快地构建功能。
- 更少的错误: 由于类型是共享的,TypeScript会立即告诉我如果我在某处有误。如果我在后端函数中更改了返回的数据类型,我的前端代码会在编辑器中立刻显示出错信息,甚至在我运行应用程序之前。
- 更简单的编码: 我的代码编辑器可以正确地自动完成前端API调用。像React Query这样的工具可以轻松处理数据获取中的复杂部分,如缓存和加载状态,因为tRPC与它们配合得很好。我不再需要Redux来处理服务器数据。
现在我怎么用它
我现在几乎什么东西都用tRPC来做,包括UserJot的整个后端。当我开发像 createSubmission
这样的后端程序时,我马上就能在前端得到既好用又类型安全的函数。这不仅仅是为了小项目,它也能够很好地支持大规模的应用开发。
tRPC 可能不适合的情况
当你同时控制前端和后端时,tRPC 就非常棒。它能很好地将两者联系起来。两者都使用 TypeScript(或 JavaScript)时,tRPC 表现得非常出色。
不过,如果你正在为他人使用的公共 API(一个公共 API),REST 或 GraphQL 可能更适合。这些都是不同系统(你无法管控的系统)之间更标准的交流方式。
要不要试试?
如果你正在使用 TypeScript 构建全栈应用,并且觉得花费了太多时间来同步前后端类型,不妨考虑一下 tRPC。它确实让我的开发体验变得更加流畅和高效,特别是在构建和扩展类似 UserJot 的工具时,我可没时间在重复定义类型或调试类型不匹配上浪费时间。
此外,如果你在找一个客户反馈工具,我正在做UserJot,非常想听听你的看法。
共同學(xué)習(xí),寫下你的評(píng)論
評(píng)論加載中...
作者其他優(yōu)質(zhì)文章