🌙 tRPC 简介
tRPC(TypeScript Remote Procedure Call)是一个用于构建类型安全的 API 的库。它允许你在客户端和服务器之间共享类型定义,从而减少重复代码并提高开发效率。tRPC 的主要特点包括:
- 类型安全:通过 TypeScript 类型系统,确保客户端和服务器之间的数据类型一致。
- 自动推断类型:自动推断 API 的类型定义,减少手动编写类型的工作量。
- 高性能:使用 HTTP 协议进行通信,支持多种传输方式(如 REST、GraphQL)。
- 简单易用:提供简洁的 API,易于集成和使用。
- 可扩展性:支持中间件、错误处理和自定义解析器等扩展功能。
🌙 tRPC 快速入门
🌙 1. 安装 tRPC
首先,你需要安装 tRPC 及其相关依赖:
npm install @trpc/server @trpc/client @trpc/react @trpc/next @trpc/react-query
1
🌙 2. 创建 tRPC 服务器
创建一个简单的 tRPC 服务器。假设你使用的是 Next.js 项目:
// server/trpc.ts
import { initTRPC } from '@trpc/server';
import superjson from 'superjson';
const t = initTRPC.create({
transformer: superjson,
});
export const router = t.router;
export const publicProcedure = t.procedure;
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
🌙 3. 定义 API 路由
定义一些简单的 API 路由:
// server/api/index.ts
import { router, publicProcedure } from '../trpc';
export const appRouter = router({
hello: publicProcedure
.input(z.object({ text: z.string().nullish() }))
.query(({ input }) => {
return {
greeting: `hello ${input?.text ?? 'world'}`,
};
}),
});
export type AppRouter = typeof appRouter;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
🌙 4. 创建 tRPC 客户端
在客户端创建 tRPC 客户端:
// utils/trpc.ts
import { createTRPCNext } from '@trpc/next';
import { AppRouter } from '../server/api/index';
export const trpc = createTRPCNext<AppRouter>({
config() {
return {
url: '/api/trpc',
};
},
});
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
🌙 5. 配置 API 路由
在 Next.js 中配置 API 路由:
// pages/api/trpc/[trpc].ts
import { createNextApiHandler } from '@trpc/server/adapters/next';
import { appRouter } from '../../../server/api/index';
export default createNextApiHandler({
router: appRouter,
createContext: () => ({}),
});
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
🌙 6. 使用 tRPC 在客户端调用 API
在客户端组件中使用 tRPC 调用 API:
// pages/index.tsx
import { trpc } from '../utils/trpc';
const HomePage = () => {
const hello = trpc.hello.useQuery({ text: 'tRPC' });
if (hello.isLoading) {
return <div>Loading...</div>;
}
if (hello.isError) {
return <div>Error: {hello.error.message}</div>;
}
return (
<div>
<h1>{hello.data?.greeting}</h1>
</div>
);
};
export default HomePage;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
🌙 tRPC + Prisma 实践
🌙 1. 安装 Prisma
首先,安装 Prisma 及其相关依赖:
npm install prisma @prisma/client
1
🌙 2. 初始化 Prisma
初始化 Prisma 并配置数据库连接:
npx prisma init
1
编辑 prisma/schema.prisma
文件以定义你的数据模型:
// prisma/schema.prisma
datasource db {
provider = "sqlite"
url = "file:./dev.db"
}
generator client {
provider = "prisma-client-js"
}
model User {
id Int @id @default(autoincrement())
name String
email String @unique
age Int?
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
🌙 3. 生成 Prisma 客户端
运行以下命令生成 Prisma 客户端:
npx prisma generate
1
🌙 4. 定义 tRPC Schema 和 Prisma 模型
为 Prisma 模型定义相应的 tRPC schema。这有助于在应用层进行数据校验,并确保类型一致性。例如:
// schemas/userSchema.ts
import { z } from 'zod';
export const userSchema = z.object({
id: z.number().int().positive().optional(),
name: z.string().min(1, { message: 'Name is required' }),
email: z.string().email({ message: 'Invalid email format' }),
age: z.number().int().positive().optional().default(18),
});
export type User = z.infer<typeof userSchema>;
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
🌙 5. 创建 tRPC API 路由
创建 tRPC API 路由以处理用户相关的操作:
// server/api/user.ts
import { router, publicProcedure } from '../trpc';
import { PrismaClient } from '@prisma/client';
import { userSchema, User } from '../../schemas/userSchema';
const prisma = new PrismaClient();
export const userRouter = router({
getAllUsers: publicProcedure.query(async () => {
return await prisma.user.findMany();
}),
createUser: publicProcedure
.input(userSchema)
.mutation(async ({ input }) => {
return await prisma.user.create({ data: input });
}),
getUserById: publicProcedure
.input(z.object({ id: z.number().int().positive() }))
.query(async ({ input }) => {
return await prisma.user.findUnique({ where: { id: input.id } });
}),
updateUser: publicProcedure
.input(userSchema)
.mutation(async ({ input }) => {
return await prisma.user.update({
where: { id: input.id },
data: input,
});
}),
deleteUser: publicProcedure
.input(z.object({ id: z.number().int().positive() }))
.mutation(async ({ input }) => {
return await prisma.user.delete({ where: { id: input.id } });
}),
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
🌙 6. 合并 tRPC 路由
将所有 tRPC 路由合并到主路由中:
// server/api/index.ts
import { router } from '../trpc';
import { userRouter } from './user';
export const appRouter = router({
user: userRouter,
});
export type AppRouter = typeof appRouter;
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
🌙 7. 使用 tRPC 在客户端调用 API
在客户端组件中使用 tRPC 调用用户相关的 API:
// pages/users.tsx
import { trpc } from '../utils/trpc';
const UsersPage = () => {
const usersQuery = trpc.user.getAllUsers.useQuery();
const createUserMutation = trpc.user.createUser.useMutation();
const deleteUserMutation = trpc.user.deleteUser.useMutation();
const handleCreateUser = async () => {
const newUser = { name: 'Alice', email: 'alice@example.com', age: 25 };
await createUserMutation.mutateAsync(newUser);
};
const handleDeleteUser = async (id: number) => {
await deleteUserMutation.mutateAsync({ id });
};
if (usersQuery.isLoading) {
return <div>Loading...</div>;
}
if (usersQuery.isError) {
return <div>Error: {usersQuery.error.message}</div>;
}
return (
<div>
<button onClick={handleCreateUser}>Create User</button>
<ul>
{usersQuery.data.map((user) => (
<li key={user.id}>
{user.name} - {user.email}
<button onClick={() => handleDeleteUser(user.id)}>Delete</button>
</li>
))}
</ul>
</div>
);
};
export default UsersPage;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
🌙 总结
通过结合使用 tRPC 和 Prisma,你可以在客户端和服务器之间保持一致的类型定义和校验逻辑,从而提高代码的健壮性和可维护性。