EasyStarter logoEasyStarter

API 开发

使用 oRPC 构建类型安全的 API

概述

EasyStarter 使用 oRPC 进行端到端类型安全的 API 开发,结合了 tRPC 和 OpenAPI 的优点。

架构

apps/server/src/
├── routers/           # API 路由
│   ├── index.ts       # 主路由
│   └── user.ts        # 用户相关过程
├── handlers/          # 业务逻辑
└── middlewares/       # 认证、日志等

创建路由

定义过程

// apps/server/src/routers/post.ts
import { publicProcedure, protectedProcedure, router } from "../lib/orpc";
import { z } from "zod";

export const postRouter = router({
  // 公开过程
  list: publicProcedure
    .input(
      z.object({
        page: z.number().default(1),
        limit: z.number().default(10),
      }),
    )
    .query(async ({ input, ctx }) => {
      return ctx.db.select().from(posts).limit(input.limit);
    }),

  // 受保护过程(需要认证)
  create: protectedProcedure
    .input(
      z.object({
        title: z.string().min(1),
        content: z.string(),
      }),
    )
    .mutation(async ({ input, ctx }) => {
      return ctx.db.insert(posts).values({
        ...input,
        authorId: ctx.user.id,
      });
    }),
});

注册路由

// apps/server/src/routers/index.ts
import { postRouter } from "./post";

export const appRouter = router({
  post: postRouter,
  // ... 其他路由
});

export type AppRouter = typeof appRouter;

前端使用

配置客户端

// apps/web/src/lib/api.ts
import { createORPCClient } from "@orpc/client";
import type { AppRouter } from "@server/routers";

export const api = createORPCClient<AppRouter>({
  baseURL: import.meta.env.VITE_SERVER_URL,
});

查询数据

// 使用 React Query
const { data, isLoading } = api.post.list.useQuery({
  page: 1,
  limit: 10,
});

修改数据

const createPost = api.post.create.useMutation({
  onSuccess: () => {
    queryClient.invalidateQueries(["post", "list"]);
  },
});

await createPost.mutateAsync({
  title: "Hello World",
  content: "我的第一篇文章",
});

中间件

认证中间件

const authMiddleware = middleware(async ({ ctx, next }) => {
  const session = await getSession(ctx.request);
  if (!session) {
    throw new ORPCError("UNAUTHORIZED");
  }
  return next({ ctx: { ...ctx, user: session.user } });
});

export const protectedProcedure = publicProcedure.use(authMiddleware);

错误处理

import { ORPCError } from "@orpc/server";

throw new ORPCError("NOT_FOUND", { message: "文章未找到" });
throw new ORPCError("FORBIDDEN", { message: "访问被拒绝" });
throw new ORPCError("BAD_REQUEST", { message: "输入无效" });

On this page