之前写过一篇关于tRPC的文章 还在前后端分离?来试试tRPC与Next.js吧! - 掘金 (juejin.cn),评论里有同学问在前后端分离的项目中可以使用吗,钻研了一下在前后端分开的项目中的如何使用trpc。
最简单的方式应该直接把server上传为包(没试过),然后让客户端安装就行,但是这样可能会让一些自动生成的类型的库无效比如prisma。所以还得优化一下
创建示例项目
首先先创建两个仓库的文件,client和server,执行pnpm init初始化项目,都创建一个index.ts文件
server
pnpm i typescript @trpc/server zod
tsconfig.json
文件,我们制定编译后js代码的输出路径为./dist
,类型声明的输出路径为./types
,strict必须设置为true。{
"compilerOptions": {
"composite": true,
"strict": true,
"outDir": "./dist",
"declarationDir": "./types",
"removeComments": false
},
"include": ["./"]
}
import { initTRPC } from '@trpc/server';
import { z } from 'zod';
import { createHTTPServer } from '@trpc/server/adapters/standalone';
const t = initTRPC.create();
export const router = t.router;
export const publicProcedure = t.procedure;
const appRouter = router({
/** 这是测试!! */
test: publicProcedure
.input(
z.object({
field1: z.number(),
field2: z.string(),
}),
)
.query(async () => {
return {
date: new Date(),
number: 1,
string: 1,
nested: {
a: 1,
},
array: [],
};
}),
});
export type AppRouter = typeof appRouter;
const server = createHTTPServer({
router: appRouter,
});
server.listen(3000);
tsc
,编译整个项目。得到编译后文件types/index.d.ts
文件,client
pnpm install typescript @trpc/client
pnpm link ../server
直接模拟安装了server的依赖(lotus-trpc-server),npm也可以直接把gihub的仓库当成依赖,不需要发包。import { createTRPCProxyClient, httpBatchLink } from '@trpc/client';
import type { AppRouter } from 'lotus-trpc-server/types'; // 从后端拿到的AppRouter
type ErrorShape = AppRouter['_def']['_config']['$types']['errorShape'];
const trpc = createTRPCProxyClient({
links: [
httpBatchLink({
url: 'http://localhost:3000',
}),
],
});
trpc.test
.query({})
.then((res) => {})
.catch((err: ErrorShape) => {});
可以看到catch这里我搞了个骚操作,把后端的error类型拿到了。
后端的注释文档也是正常显示的
ok,一切运行正常,非常完美!在我的另一个使用prisma的项目中也进行了测试,可以正常拿到prisma的类型。
其他
可能会有人觉得ts的类型有时候不够直观,完全看不出要传什么
可以参考一下这个类型工具,可以把ide提示的字段扩展开,更直观看到有什么字段visual studio code - How can I see the full expanded contract of a Typescript type? - Stack Overflow
export type Expand = T extends (...args: infer A) => infer R
? (...args: Expand) => Expand
: T extends infer O
? { [K in keyof O]: O[K] }
: never;
export type ExpandRecursively = T extends (...args: infer A) => infer R
? (...args: ExpandRecursively) => ExpandRecursively
: T extends object
? T extends infer O
? { [K in keyof O]: ExpandRecursively }
: never
: T;
使用前:
使用后: