跳至主要内容

一篇带有“trpc”标签的文章

查看所有标签

·阅读时长 5 分钟
Alex / KATT 🐱

我是 Alex,GitHub 上的昵称是“KATT”,我想告诉你一个名为 tRPC 的库。我还没有发布任何关于它的文章,所以我就写了这篇介绍来开始(但我们已经在 GitHub 上获得了超过 530 个 🌟)。期待文章和视频介绍!如果你想随时了解最新消息或有任何问题,可以在 Twitter 上关注我 @alexdotjs

简而言之,tRPC 为你提供了从(节点)服务器到客户端的端到端类型安全,无需声明类型。你只需要在后端返回函数中的数据,在前端根据端点名称使用该数据。

以下是使用 tRPC 端点和客户端调用时的示例: Alt Text

我为 React 创建了一个库(@trpc/react),它建立在优秀的 react-query 之上,但客户端库(@trpc/client)无需 React 即可使用(如果你想构建一个特定的 Svelte/Vue/Angular/[..]库,请联系我!)

无需代码生成,你可以轻松地将其添加到现有的 Next.js/CRA/Express 项目中。

示例

以下是一个名为 hello 的 tRPC 过程(又名端点)的示例,它接受一个 string 参数。

tsx
const appRouter = trpc.router().query('hello', {
input: z.string().optional(),
resolve: ({ input }) => {
return {
text: `hello ${input ?? 'world'}`,
};
},
});
export type AppRouter = typeof appRouter;
tsx
const appRouter = trpc.router().query('hello', {
input: z.string().optional(),
resolve: ({ input }) => {
return {
text: `hello ${input ?? 'world'}`,
};
},
});
export type AppRouter = typeof appRouter;

以下是一个使用该数据的类型安全的客户端

tsx
import type { AppRouter } from './server';
async function main() {
const client = createTRPCClient<AppRouter>({
url: `http://localhost:2022`,
});
const result = await client.query('hello', '@alexdotjs');
console.log(result); // --> { text: "hello @alexdotjs" }
}
main();
tsx
import type { AppRouter } from './server';
async function main() {
const client = createTRPCClient<AppRouter>({
url: `http://localhost:2022`,
});
const result = await client.query('hello', '@alexdotjs');
console.log(result); // --> { text: "hello @alexdotjs" }
}
main();

这就是你获得类型安全所需的一切!result 的类型是从后端在函数中返回的内容推断出来的。输入数据的类型也从验证器的返回值推断出来,因此数据可以直接安全使用 - 实际上,你必须通过验证器传递输入数据(tRPC 与 zod/yup/自定义验证器开箱即用)。

以下是一个 CodeSandbox 链接,你可以在其中使用上面的示例:https://githubbox.com/trpc/trpc/tree/next/examples/standalone-server(查看终端输出而不是预览!)

什么?我从后端导入代码到客户端? - 不,你实际上没有

即使看起来像这样,也没有代码从服务器共享到客户端;TypeScript 的 import type "[..]只导入声明以用于类型注释和声明。它总是被完全擦除,因此在运行时没有它的残留。" - 这是 TypeScript 3.8 中添加的功能 - 查看 TypeScript 文档

无需代码生成,只要你有一种方法可以从服务器共享类型到客户端(希望你已经在使用单仓库),你就可以在今天将它添加到你的应用程序中。

但我们才刚刚开始!

我之前提到过有一个 React 库,在 React 中使用上面的数据,你需要这样做

tsx
const { data } = trpc.useQuery(['hello', '@alexdotjs']);
tsx
const { data } = trpc.useQuery(['hello', '@alexdotjs']);

...你将在客户端获得类型安全的数据。

你可以在今天将 tRPC 添加到现有的遗留项目中(为 Express/Next.js 提供了适配器),它可以与 CRA 很好地配合使用,并且应该也可以与 React Native 配合使用。它甚至不依赖于 React,所以如果你想做一个 Svelte 或 Vue 库,请联系我。

如何处理数据变动?

变动与查询一样简单,它们在底层实际上是一样的,只是作为语法糖以不同的方式暴露出来,并生成 HTTP POST 请求而不是 GET 请求。

以下是一个更复杂的示例,它使用数据库,取自我们在 todomvc.trpc.io 上的 TodoMVC 示例 / https://github.com/trpc/trpc/tree/next/examples/next-prisma-todomvc

tsx
const todoRouter = createRouter().mutation('add', {
input: z.object({
id: z.string().uuid(),
data: z.object({
completed: z.boolean().optional(),
text: z.string().min(1).optional(),
}),
}),
async resolve({ ctx, input }) {
const { id, data } = input;
const todo = await ctx.task.update({
where: { id },
data,
});
return todo;
},
});
tsx
const todoRouter = createRouter().mutation('add', {
input: z.object({
id: z.string().uuid(),
data: z.object({
completed: z.boolean().optional(),
text: z.string().min(1).optional(),
}),
}),
async resolve({ ctx, input }) {
const { id, data } = input;
const todo = await ctx.task.update({
where: { id },
data,
});
return todo;
},
});

React 使用看起来像这样

tsx
const addTask = trpc.useMutation('todos.add');
return (
<>
<input
placeholder="What needs to be done?"
onKeyDown={(e) => {
const text = e.currentTarget.value.trim();
if (e.key === 'Enter' && text) {
addTask.mutate({ text });
e.currentTarget.value = '';
}
}}
/>
</>
)
tsx
const addTask = trpc.useMutation('todos.add');
return (
<>
<input
placeholder="What needs to be done?"
onKeyDown={(e) => {
const text = e.currentTarget.value.trim();
if (e.key === 'Enter' && text) {
addTask.mutate({ text });
e.currentTarget.value = '';
}
}}
/>
</>
)

暂时结束。

总之,正如我所说,我只是想开始。还有很多事情

  • 为传入请求创建上下文以获取用户特定数据,这些数据被依赖注入到解析器中 - 链接
  • 路由器中间件支持 - 链接
  • 合并路由器(你可能不希望所有后端数据都在一个文件中) - 链接
  • 在 React 世界中,你见过的最简单的服务器端渲染,使用我们的 @trpc/next 适配器 - 链接
  • 类型安全的错误格式化 - 链接
  • 数据转换器(在网络上传输 Date/Map/Set 对象) - 链接
  • React Query 的助手

如果你想开始,在 Next.js 入门 中有一些示例。

在 Twitter 上关注我以获取更新!