之前写过一篇关于tRPC的文章 还在前后端分离?来试试tRPC与Next.js吧! - 掘金 (juejin.cn),评论里有同学问在前后端分离的项目中可以使用吗,钻研了一下在前后端分开的项目中的如何使用trpc。
最简单的方式应该直接把server上传为包(没试过),然后让客户端安装就行,但是这样可能会让一些自动生成的类型的库无效比如prisma。所以还得优化一下
创建示例项目
首先先创建两个仓库的文件,client和server,执行pnpm init初始化项目,都创建一个index.ts文件
server
- server 安装依赖
pnpm i typescript @trpc/server zod
- 创建
tsconfig.json
文件,我们制定编译后js代码的输出路径为./dist
,类型声明的输出路径为./types
,strict必须设置为true。
json
{
"compilerOptions": {
"composite": true,
"strict": true,
"outDir": "./dist",
"declarationDir": "./types",
"removeComments": false
},
"include": ["./"]
}
- 复制官方实例,简单写一个trpc程序
ts
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
- 把server发包有点麻烦了,这里为了方便我们直接使用
pnpm link ../server
直接模拟安装了server的依赖(lotus-trpc-server),npm也可以直接把gihub的仓库当成依赖,不需要发包。
- 照着官方示例写点代码,并且AppRouter的类型从server获取
ts
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<AppRouter>({
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
ts
export type Expand<T> = T extends (...args: infer A) => infer R
? (...args: Expand<A>) => Expand<R>
: T extends infer O
? { [K in keyof O]: O[K] }
: never;
export type ExpandRecursively<T> = T extends (...args: infer A) => infer R
? (...args: ExpandRecursively<A>) => ExpandRecursively<R>
: T extends object
? T extends infer O
? { [K in keyof O]: ExpandRecursively<O[K]> }
: never
: T;
使用前:
使用后: