mcpField
O
orpc-fullstack

orpc-fullstack

github.com/stephen-golban开发38 次安装
代码仓库

关于 orpc-fullstack

面向使用 oRPC 构建 TypeScript API 的开发者,提供契约、过程、路由、中间件和客户端的实践指南。内容还涉及 Hono、TanStack Query、实时事件与错误处理。

主要功能

  1. 01契约优先的 API 开发
  2. 02输入输出验证与中间件
  3. 03类型化错误处理
  4. 04Hono 服务端集成
  5. 05TanStack Query 数据获取
  6. 06SSE 与事件迭代器

适用场景

  1. 01按契约实现 API 路由
  2. 02为过程添加身份验证和授权
  3. 03通过 TanStack Query 获取数据
  4. 04构建 SSE 流式功能

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/stephen-golban/orpc-fullstack --skill orpc-fullstack

安装后在 Claude Code 中输入/orpc-fullstack确认已加载。

附带文件references/REFERENCE.md

详细介绍

oRPC — 轻松构建类型安全的 API

oRPC 将 RPC 与 OpenAPI 相结合,提供端到端类型安全的 API(v1.12+)。它支持 Zod、Valibot、Arktype 或任何 Standard Schema 库。

适用场景

在以下情况参考这些指南:

  • 创建或修改 API 过程(契约、路由器、处理程序)
  • 设置中间件(身份验证、授权守卫、日志记录)
  • 将 oRPC 与 Hono 或其他服务端框架集成
  • 使用 TanStack Query 构建数据获取钩子
  • 使用事件迭代器 / SSE 实现实时功能
  • 配置客户端链接(fetch、WebSocket、批处理、重试)
  • 处理错误(服务端 ORPCError、客户端类型化错误)

核心概念

概念导入项用途
契约@orpc/contract 中的 oc定义 API 结构,无需处理程序
过程@orpc/server 中的 os结合验证、中间件和 DI 的函数
路由器普通对象将过程组合成树状结构
中间件os.middleware()拦截请求、注入上下文、限制访问
处理程序OpenAPIHandler / RPCHandler通过 HTTP/WS 提供过程
客户端createORPCClient + 链接根据契约 / 路由器创建类型安全的客户端
TanStack QuerycreateTanstackQueryUtils用于查询 / 修改操作的 React 钩子

项目结构

按领域模块组织,并为每个模块配对契约与路由器文件:

src/
  index.ts                    # Hono app + handler setup
  middlewares/
    auth-middleware.ts         # Session validation -> injects user
  modules/
    contract.ts               # Root barrel: all contracts
    router.ts                 # Root barrel: all routers
    health/
      health.contract.ts
      health.router.ts
    user/
      user.contract.ts
      user.router.ts

根目录的 barrel 文件组合各模块:export default { health, user }。

契约优先开发

契约定义 API 结构。路由器通过 TypeScript 强制检查来实现契约。

// Contract — define shape
import { oc } from "@orpc/contract";
import { z } from "zod/v4";

const userContract = oc
  .route({ tags: ["user"] })
  .errors({ UNAUTHORIZED: {} });

const searchUser = userContract
  .route({ method: "POST", path: "/user/search" })
  .input(z.object({ query: z.string() }))
  .output(z.array(userSchema));

export default { searchUser };
// Router — implement contract
import { implement } from "@orpc/server";
import contract from "./user.contract";

const router = implement(contract).$context<{ headers: Headers }>();

const searchUser = router.searchUser
  .use(authMiddleware)
  .handler(async ({ input, context }) => { /* ... */ });

export default { searchUser };

过程

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

const example = os
  .use(aMiddleware)                         // Middleware
  .input(z.object({ name: z.string() }))   // Validate input
  .output(z.object({ id: z.number() }))    // Validate output (recommended)
  .handler(async ({ input, context }) => {  // Handler
    return { id: 1 };
  });
  • .handler 是唯一必需的步骤
  • 指定 .output 可提高 TypeScript 类型推断速度
  • 创建可复用的基础过程:const protectedProcedure = os.$context<Ctx>().use(authMiddleware)

中间件

身份验证中间件会将用户注入上下文:

export const authMiddleware = os
  .$context<{ headers: Headers }>()
  .middleware(async ({ context, next }) => {
    const session = await auth.api.getSession({ headers: context.headers });
    if (!session) throw new ORPCError("UNAUTHORIZED");
    return next({ context: { ...context, user: session.user } });
  });

用于授权守卫的输入感知型中间件:

export const membershipGuard = os
  .$context<{ user: User }>()
  .middleware(async ({ context, next }, input: { uuid: string }) => {
    // Check membership using input.uuid + context.user.id
    if (!member) throw new ORPCError("FORBIDDEN");
    return next();
  });

中间件按从左到右的顺序堆叠:.use(auth).use(guard).handler(...)。

内置中间件:onStart、onSuccess、onError、onFinish、dedupeMiddleware。

错误处理

// Server — throw errors
throw new ORPCError("NOT_FOUND");
throw new ORPCError("BAD_REQUEST", { message: "Invalid input" });

// Contract-defined typed errors
const contract = oc.errors({
  RATE_LIMITED: { data: z.object({ retryAfter: z.number() }) },
});

// Handler uses typed factory
const proc = implement(contract).handler(async ({ errors }) => {
  throw errors.RATE_LIMITED({ data: { retryAfter: 60 } });
});

// Client — handle errors
const [error, data] = await safe(client.doSomething({ id: "123" }));
if (isDefinedError(error)) { /* typed from contract */ }

Hono 集成

import { OpenAPIHandler } from "@orpc/openapi/fetch";
import { Hono } from "hono";

const handler = new OpenAPIHandler(router, { /* plugins, interceptors */ });

const app = new Hono()
  .basePath("/api")
  .use("/rpc/*", async (c, next) => {
    const { matched, response } = await handler.handle(c.req.raw, {
      prefix: "/api/rpc",
      context: { headers: c.req.raw.headers },
    });
    if (matched) return c.newResponse(response.body, response);
    await next();
  });

TanStack Query

import { createTanstackQueryUtils } from "@orpc/tanstack-query";
const orpc = createTanstackQueryUtils(client);

// Queries
useQuery(orpc.user.search.queryOptions({ input: { query } }));

// Mutations
useMutation(orpc.vehicle.add.mutationOptions());

// Infinite queries
useInfiniteQuery(orpc.feed.list.infiniteOptions({
  input: (pageParam) => ({ cursor: pageParam, limit: 20 }),
  initialPageParam: undefined,
  getNextPageParam: (lastPage) => lastPage.nextCursor,
}));

// Keys for invalidation
orpc.vehicle.key()                           // All vehicle queries
queryClient.invalidateQueries({ queryKey: orpc.vehicle.key() });

事件迭代器(SSE / 流式传输)

// Server — async generator
const live = os
  .output(eventIterator(z.object({ message: z.string() })))
  .handler(async function* ({ signal }) {
    for await (const payload of publisher.subscribe("topic", { signal })) {
      yield payload;
    }
  });

// Client — consume
for await (const event of await client.live()) {
  console.log(event.message);
}

使用 EventPublisher 在处理程序之间实现类型化的发布 / 订阅。

客户端设置

import { RPCLink } from "@orpc/client/fetch";
import { createORPCClient } from "@orpc/client";

const link = new RPCLink({
  url: "http://localhost:3000/api/rpc",
  headers: () => ({ Authorization: `Bearer ${getToken()}` }),
});

export const client = createORPCClient(link);

WebSocket:import { RPCLink } from "@orpc/client/websocket"。

详细参考

有关完整示例、高级模式和完整 API 内容,请参阅:

  • 完整参考 — 完整文档,包含详细代码示例,涵盖契约、路由器、中间件、服务端处理程序、客户端设置、TanStack Query、事件迭代器、插件、文件上传、WebSocket、AI SDK 集成和元数据。

评论

支持 Markdown