这是一个使用 Nest.js、Prisma、tRPC 和 Zod 构建的现代化全栈应用程序的示例项目。
- NestJS + tRPC 一套完整的类型安全方案,客户端 api 接入体验拉满!
- Prisma 现代化的 ORM 框架。
- 使用 Zod 替代 class-validator,让你无需编写繁琐的装饰器。
- CASL.js 完成复杂角色权限的验证。
- Server
- NestJS
- tRPC
- Prisma
- Zod
- Web
- Next.js
- Admin
- Ant Design Pro
你可以使用传统 Controller 的方式来编写接口,也可以定义 tRPC router 的形式,取决于你喜好。我个人针对用户端部分(Next.js、React Native) 会偏向于 tRPC,而对于管理面板(Ant Design Pro)还是选用传统 Controller 方式。
- 克隆该项目
git clone https://github.com/kuizuo/nest-trpc-prisma-starter
- 配置环境变量,将 .env.example 更改为 .env,并配置好 postgresql 数据库变量。
- 执行如下代码
pnpm i
pnpm dev
将会启动以下服务
Server: http://127.0.0.1:5001
Trpc placground: http://127.0.0.1:5001/api/trpc-playground
Admin: http://localhost:8000
实现代码参考
-
假定你的 module 为 xxx,在 modules/xxx 文件夹下创建 xxx.trpc.ts 文件,其代码可以参考 todo.trpc.ts 文件。这里贴上示例代码
@TRPCRouter() @Injectable() export class TodoTrpcRouter implements OnModuleInit { private router: ReturnType<typeof this.createRouter> constructor( private readonly trpcService: TRPCService, private readonly todoService: TodoService, ) { } onModuleInit() { this.router = this.createRouter() } private createRouter() { const procedureAuth = this.trpcService.procedureAuth return defineTrpcRouter('todo', { list: this.trpcService.procedureAuth .input(TodoPagerDto.schema) .meta({ model: 'Todo', action: Action.Read }) .query(async (opt) => { const { input, ctx: { user } } = opt return this.todoService.list(input, user.id) }), create: procedureAuth .input(TodoInputSchema) .meta({ model: 'Todo', action: Action.Create }) .mutation(async (opt) => { const { input, ctx: { user } } = opt return this.todoService.create(input, user.id) }), } }
-
将 xxx.trpc.ts 在 xxx.module.ts 声明导入,同时在 trpc.routers.ts 导入用于类型提示生成。
-
此时便可在 client 端接入 trpc server,这部分请参阅 tRPC 文档。
-
首先要定义所访问的资源,也就是 Prisma 的 model,可在 ability.class.ts 中查看详情,这里假定你的模块为
XXX
。 -
在指定 module 中,创建 xxx.ability.ts,可以仿造 todo.ability.ts 进行编写。
-
记得将 xxxAbility 作为 Provider 导入到 xxxModule 中,CaslModule 会自动扫描所有 ability 注入服务。
-
在 Controller 中使用装饰器
@UseGuards(PolicyGuard)
,同样可参见 todo.controller.ts 。并在指定控制器方法中 使用@Policy
来声明该路由请求者所需的权限。export class TodoController { @Get(':id') @Policy({ model: 'Todo', action: Action.Read }) async findOne(@Param() { id }: IdDto) { return this.todoService.findOne(id) } }
-
trpc 则使用
.meta
,如下所示。defineTrpcRouter('todo', { byId: procedureAuth .input(IdDto.schema) .meta({ model: 'Todo', action: Action.Read }) .query(async (opt) => { const { input } = opt const { id } = input return this.todoService.findOne(id) }), }
由于项目使用 Monorepo 进行管理,因此你可以打开 .vscode/project.code-workspace,点击右下角 Open Workplace 打开多根工作区,如下图所示。
- 示例从 Todo List 更改成 Post
- 升级到 Trpc 11
- 升级 Tanstack Query 5
- 集成 Auth.js
Youni 一个基于 React Native 开发的校园社交应用。
http://blog.innei.ren/nestjs-with-trpc-and-dependency-injection
https://www.tomray.dev/nestjs-nextjs-trpc
Copyright (c) 2024 Kuizuo