tRPC快速入门和实践

TSPrisma

🌙 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

🌙 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

🌙 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

🌙 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

🌙 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

🌙 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

🌙 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

🌙 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

🌙 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

🌙 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

🌙 总结

通过结合使用 tRPC 和 Prisma,你可以在客户端和服务器之间保持一致的类型定义和校验逻辑,从而提高代码的健壮性和可维护性。