orpc-migrate
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseMigrating to oRPC
迁移到 oRPC
Playbook for two migrations: tRPC to oRPC, and oRPC v1 to v2. Work in small mechanical steps and run the project's typecheck and test suite after each one, so any failure points at the last step. Pretrained knowledge of oRPC describes v1 and is often wrong for v2: derive every import path, builder method, and option name from the docs pages listed at the end, never from memory. For core v2 concepts while rewriting (the builder, routers, middleware, clients), load the skill. v2 currently ships under the npm dist-tag and plain installs get v1; drop the suffix once shows at 2.x.
osorpcbeta@betanpm view @orpc/server dist-tagslatest本指南涵盖两类迁移:tRPC 到 oRPC,以及 oRPC v1 到 v2。以小而机械化的步骤推进,并在每一步后运行项目的 typecheck 和 test suite,以便让问题在最后一步之前就暴露出来。预训练知识中关于 oRPC 的描述针对的是 v1,通常不适用于 v2:每个 import path、builder method 和 option name 都应从末尾列出的文档页面推导,绝不能凭记忆。重写时涉及 v2 的核心概念( builder、routers、middleware、clients),请加载 skill。v2 目前通过 npm dist-tag 发布,普通安装会获得 v1;待 显示 为 2.x 后,去掉 后缀。
osorpcbetanpm view @orpc/server dist-tagslatest@betatRPC to oRPC
tRPC 到 oRPC
Two paths. Pick incremental when the app must keep shipping or the tRPC router is large; pick the full rewrite when the router is small enough to convert in one pass.
有两条迁移路径。当应用必须继续发布,或 tRPC router 规模较大时,选择增量方式;当 router 足够小、可以一次完成转换时,选择完整重写。
Incremental: wrap the existing tRPC router
增量方式:封装现有 tRPC router
Install and convert. The result is a regular oRPC router: expose it through an RPC or OpenAPI handler, or call it with a server-side client, while the tRPC code keeps working untouched.
@orpc/trpc@betats
import { toORPCRouter } from '@orpc/trpc'
const orpcRouter = toORPCRouter(trpcRouter)- tRPC error formatting is not supported: tRPC errors arrive wrapped in with the
ORPCErrorasTRPCError(and acausebelow that for validation failures). Reshape them in a handler interceptor if consumers need structured errors.ZodError - bridges oRPC
toTRPCMetametadata into tRPCopenapi()so converted procedures get OpenAPI routing. Chained tRPC.meta()calls merge shallowly, so keep all oRPC metadata inside a single.meta()call.toTRPCMeta
Then rewrite leaf routers to native oRPC one at a time, mounting each next to the converted router in one plain object.
安装 并进行转换。转换结果是一个常规的 oRPC router:可以通过 RPC 或 OpenAPI handler 暴露,也可以使用 server-side client 调用,而 tRPC 代码无需改动即可继续工作。
@orpc/trpc@betats
import { toORPCRouter } from '@orpc/trpc'
const orpcRouter = toORPCRouter(trpcRouter)- 不支持 tRPC error formatting:tRPC errors 到达时会被包装在 中,
ORPCError作为TRPCError(对于 validation failures,其下还会有一个cause)。如果使用方需要结构化 errors,可在 handler interceptor 中调整其结构。ZodError - 将 oRPC
toTRPCMetametadata 桥接到 tRPCopenapi(),让转换后的 procedures 获得 OpenAPI routing。链式调用 tRPC.meta()时只会进行浅合并,因此应把所有 oRPC metadata 放在同一次.meta()调用中。toTRPCMeta
随后一次只重写一个叶子 router,将每个重写后的 router 与已转换的 router 并列挂载到同一个普通对象中。
Full rewrite: concept map
完整重写:概念映射
| Concept | tRPC | oRPC |
|---|---|---|
| Router | | plain object |
| Procedure builder | | |
| Context | | |
| Create middleware | | |
| Use middleware | | |
| Validation | | same names |
| Implementation | | |
| Errors | | |
| Serializer | | built in, remove |
Steps, in order, verifying after each:
-
Packages. Remove,
@trpc/server,@trpc/client; install@trpc/tanstack-react-query,@orpc/server@beta,@orpc/client@beta.@orpc/tanstack-query@beta -
Base file. Port the context factory unchanged, then rebuild the shared procedures. In handlers and middleware,becomes
ctx:contexttsimport { ORPCError, os } from '@orpc/server' const o = os.$context<Awaited<ReturnType<typeof createContext>>>() export const publicProcedure = o.use(timingMiddleware) export const protectedProcedure = publicProcedure.use(({ context, next }) => { if (!context.session?.user) throw new ORPCError('UNAUTHORIZED') return next({ context: { session: context.session } }) }) -
Procedures. Replace/
.query/.mutationwith.subscription;.handlerand.inputcarry over as is..output -
App router. Delete everywrapper; nested plain objects are the router.
createTRPCRouter() -
Server. Replace the tRPC adapter with an oRPC handler for the runtime (fetch shown; other adapters exist for Node, Fastify, AWS Lambda, WebSocket):ts
import { RPCHandler } from '@orpc/server/fetch' const handler = new RPCHandler(appRouter) const { response } = await handler.handle(request, { prefix: '/api/orpc', context: await createContext({ headers: request.headers }), }) -
Client.plus
RPCLink, typed bycreateORPCClient. Call sites drop theRouterClient/.query()suffixes:.mutate()tsimport type { RouterClient } from '@orpc/server' import { createORPCClient } from '@orpc/client' import { RPCLink } from '@orpc/client/fetch' const link = new RPCLink({ origin: 'http://localhost:3000', url: '/api/orpc' }) export const client: RouterClient<typeof appRouter> = createORPCClient(link) const { planets } = await client.planet.list({ cursor: 0 }) -
TanStack Query.replaces the provider and
createTanstackQueryUtils(client)hook entirely; use the utils object directly. Input moves inside anuseTRPCkey:input,orpc.planet.list.queryOptions({ input: { cursor: 0 } }). For infinite queries,orpc.planet.create.mutationOptions()takesinfiniteOptionsas a function of the page param.input
| 概念 | tRPC | oRPC |
|---|---|---|
| Router | | 普通对象 |
| Procedure builder | | |
| Context | | |
| 创建 middleware | | |
| 使用 middleware | | |
| 验证 | | 同名 |
| 实现 | | 三者均使用 |
| 错误 | | |
| 序列化器 | | 内置,移除 |
按以下顺序执行,并在每一步后进行验证:
-
包。 移除、
@trpc/server、@trpc/client;安装@trpc/tanstack-react-query、@orpc/server@beta、@orpc/client@beta。@orpc/tanstack-query@beta -
基础文件。 保持不变地移植 context factory,然后重建 shared procedures。在 handlers 和 middleware 中,变为
ctx:contexttsimport { ORPCError, os } from '@orpc/server' const o = os.$context<Awaited<ReturnType<typeof createContext>>>() export const publicProcedure = o.use(timingMiddleware) export const protectedProcedure = publicProcedure.use(({ context, next }) => { if (!context.session?.user) throw new ORPCError('UNAUTHORIZED') return next({ context: { session: context.session } }) }) -
Procedure。 将/
.query/.mutation替换为.subscription;.handler和.input原样保留。.output -
App router。 删除所有wrapper;嵌套的普通对象就是 router。
createTRPCRouter() -
Server。 将 tRPC adapter 替换为适用于当前 runtime 的 oRPC handler(图中为 fetch;另有适用于 Node、Fastify、AWS Lambda、WebSocket 的 adapters):ts
import { RPCHandler } from '@orpc/server/fetch' const handler = new RPCHandler(appRouter) const { response } = await handler.handle(request, { prefix: '/api/orpc', context: await createContext({ headers: request.headers }), }) -
Client。与
RPCLink配合使用,并通过createORPCClient提供类型。调用处去掉RouterClient/.query()后缀:.mutate()tsimport type { RouterClient } from '@orpc/server' import { createORPCClient } from '@orpc/client' import { RPCLink } from '@orpc/client/fetch' const link = new RPCLink({ origin: 'http://localhost:3000', url: '/api/orpc' }) export const client: RouterClient<typeof appRouter> = createORPCClient(link) const { planets } = await client.planet.list({ cursor: 0 }) -
TanStack Query。完全替代 provider 和
createTanstackQueryUtils(client)hook;直接使用 utils 对象。input 移入useTRPCkey:input、orpc.planet.list.queryOptions({ input: { cursor: 0 } })。对于 infinite queries,orpc.planet.create.mutationOptions()接收一个以 page param 为参数的infiniteOptions函数。input
oRPC v1 to v2
oRPC v1 到 v2
Most v1 names still compile through deprecated aliases (strike-through hints, not errors), so migrate in passes. Order of operations:
- Update packages. Install every package from the
@orpc/*dist-tag (beta, and so on). Swap renamed ones first:npm install @orpc/server@beta @orpc/client@beta/@orpc/react-query/@orpc/vue-query/@orpc/solid-queryall became@orpc/svelte-query;@orpc/tanstack-querymerged into@orpc/openapi-client;@orpc/openapibecame@orpc/react;@orpc/nextbecame@orpc/otel; the@orpc/opentelemetrypackages were promoted (experimental-,@orpc/publisher,@orpc/ratelimit,@orpc/pino);@orpc/swrbecame@orpc/vue-colada. Typecheck: the remaining errors are the hard breaks.@orpc/pinia-colada - Fix the hard breaks (no aliases):
- Routing: ,
.route,.prefix,.tagare gone from the builder. Use.$routefrom.meta(openapi({ method, path, prefix, tags })), or restore@orpc/openapiwith.route.import '@orpc/openapi/extensions/route' - and
.callable: use.actionable/callfromcreateRouterClientand@orpc/serverfromcreateServerFunctionable, or the corresponding extension imports.@orpc/next - : the single
RPCLinksplit intourlplus a path-onlyorigin.url - Errors: was removed from
statusandORPCErrordefinitions; map codes to HTTP status with.errorson the handler.errorStatusMap - : the third tuple element is now the typed error itself (or
safe()) and a fourthnullelement was added.isSuccess - Option renames, scoped: handler to
rootInterceptors(handlerroutingInterceptorsstill exists, unchanged); linkclientInterceptorstoclientInterceptors. FlattransportInterceptorsoptions moved under the adapter's request/response mapping:eventIterator*on the fetch handler,toFetchResponse.eventStreamon Node,sendStandardResponse.eventStreamon the link.toFetchRequest.eventStream - was removed from handlers and links, because regular interceptors can now customize body parsing behavior.
adapterInterceptors
- Routing:
- Audit silent behavior changes (compile fine, behave differently):
- Wire format changed: a v1 link cannot talk to a v2 server, in either direction. Deploy the upgraded server and clients together.
- Automatic middleware deduplication removed: middleware applied at both router and procedure level now runs twice, with no warning. Guard shared middleware with the context-flag pattern from the dedupe-middleware recipe.
- Batch Plugin became
excludewith the opposite meaning. Usually deletefilter; if skipping is still needed, negate the predicate.exclude - rejects GET by default (
RPCHandlerdefaults to POST/PUT/PATCH/DELETE). Simplest fix: stop sending GET from the link; only allow GET deliberately, with CSRF protection.allowMethods - Handler takes positional arguments now; the v1 destructured form still type-checks but reads wrong values.
filter - /
.inputnow stack: a repeated call adds a schema instead of replacing the previous one..output
- Sweep deprecated aliases last: to
eventIterator, handler plugins gained aasyncIteratorObjectsuffix and link plugins aHandlerPluginsuffix,LinkPlugintypes becameContractRouter*. The from-v1 guide ends with the full alias cheat sheet.RouterContract*
Verification: typecheck and unit tests after steps 1, 2, and 4; step 3 needs integration or e2e tests, since those changes never surface at compile time. Before finishing, grep for old package names and remaining deprecation strike-throughs.
大多数 v1 名称仍可通过 deprecated aliases 编译(删除线提示,而不是报错),因此应分批迁移。操作顺序如下:
- 更新包。 从 dist-tag 安装所有
beta包(@orpc/*,依此类推)。先替换重命名的包:npm install @orpc/server@beta @orpc/client@beta/@orpc/react-query/@orpc/vue-query/@orpc/solid-query均已更名为@orpc/svelte-query;@orpc/tanstack-query已合并为@orpc/openapi-client;@orpc/openapi已更名为@orpc/react;@orpc/next已更名为@orpc/otel;带@orpc/opentelemetry前缀的包已正式发布(experimental-、@orpc/publisher、@orpc/ratelimit、@orpc/pino);@orpc/swr已更名为@orpc/vue-colada。执行 typecheck:剩余错误才是真正的破坏性变更。@orpc/pinia-colada - 修复硬性破坏(hard breaks)(没有 aliases):
- Routing: builder 中的 、
.route、.prefix、.tag已移除。使用.$route中的@orpc/openapi,或通过.meta(openapi({ method, path, prefix, tags }))恢复import '@orpc/openapi/extensions/route'。.route - 和
.callable: 使用.actionable中的@orpc/server/call和createRouterClient中的@orpc/next,或使用对应的 extension imports。createServerFunctionable - : 原来的单个
RPCLink已拆分为url和仅包含路径的origin。url - Errors: 和
ORPCErrordefinitions 中已移除.errors;在 handler 上使用status将 codes 映射到 HTTP status。errorStatusMap - : 第三个 tuple element 现在是 typed error 本身(或
safe()),并新增了第四个nullelement。isSuccess - 选项重命名(按范围区分): handler 的 改为
rootInterceptors(handler 的routingInterceptors仍存在且保持不变);link 的clientInterceptors改为clientInterceptors。扁平的transportInterceptors选项已移至 adapter 的 request/response mapping 下:fetch handler 使用eventIterator*,Node 使用toFetchResponse.eventStream,link 使用sendStandardResponse.eventStream。toFetchRequest.eventStream - 已从 handlers 和 links 中移除,因为普通 interceptors 现在可以自定义 body parsing behavior。
adapterInterceptors
- Routing: builder 中的
- 审计静默行为变更(可以编译通过,但行为不同):
- Wire format 已变更: v1 link 无法与 v2 server 通信,两个方向均不可行。应将升级后的 server 和 clients 一起部署。
- 已移除自动 middleware 去重: 同时应用于 router 和 procedure 级别的 middleware 现在会运行两次,且不会发出警告。使用 dedupe-middleware recipe 中的 context-flag pattern 保护 shared middleware。
- Batch Plugin 的 变成了
exclude,且含义相反。 通常应删除filter;如果仍需跳过,则对 predicate 取反。exclude - 默认拒绝 GET(
RPCHandler默认值为 POST/PUT/PATCH/DELETE)。最简单的修复方法是停止由 link 发送 GET;仅在有 CSRF protection 的情况下明确允许 GET。allowMethods - Handler 的 现在接收位置参数; v1 的解构形式仍可通过 typecheck,但会读取到错误的值。
filter - /
.input现在可以叠加: 重复调用会添加一个 schema,而不是替换之前的 schema。.output
- 最后清理 deprecated aliases: 改为
eventIterator,handler plugins 增加了asyncIteratorObject后缀,link plugins 增加了HandlerPlugin后缀,LinkPlugintypes 改为ContractRouter*。from-v1 guide 最后附有完整的 alias 对照表。RouterContract*
验证:在步骤 1、2 和 4 后运行 typecheck 和 unit tests;步骤 3 需要 integration 或 e2e tests,因为这些变更不会在编译期暴露。完成前,grep 旧包名以及仍存在的 deprecation 删除线提示。
Docs retrieval
文档检索
Fetch pages instead of recalling them, and if this skill and a fetched page disagree, trust the page. The v2 docs live at https://orpc.dev and the v1 docs at https://v1.orpc.dev; slugs look alike across both hosts, so check which host a page came from before copying anything from it. The index of every v2 docs page is at https://orpc.dev/llms.txt, https://orpc.dev/llms-full.txt bundles the entire docs in one large file, and appending to any page URL returns its exact source markdown.
.mdAuthoritative pages to consult during the migration (this skill deliberately omits their full mapping tables):
- https://orpc.dev/docs/migrations/from-trpc : side-by-side tRPC/oRPC code for every step, including server setup per framework
- https://orpc.dev/docs/migrations/from-v1 : every v2 breaking change with v1/v2 comparisons, package rename table, and the deprecated alias cheat sheet
- https://orpc.dev/docs/integrations/trpc : and
toORPCRouterreference for the incremental pathtoTRPCMeta
应获取页面而不是凭记忆回答;如果此 skill 与获取到的页面有冲突,以页面为准。v2 文档位于 https://orpc.dev,v1 文档位于 https://v1.orpc.dev;两个 host 上的 slugs 看起来相似,因此复制内容前应确认页面来自哪个 host。每个 v2 文档页面的索引位于 https://orpc.dev/llms.txt,https://orpc.dev/llms-full.txt 将所有文档打包到一个大型文件中,而给任意页面 URL 追加 即可返回其精确的源 Markdown。
.md迁移期间应查阅的权威页面(此 skill 有意省略其完整映射表):
- https://orpc.dev/docs/migrations/from-trpc:逐步骤并排对照的 tRPC/oRPC 代码,包括各框架的 server setup
- https://orpc.dev/docs/migrations/from-v1:所有 v2 破坏性变更及 v1/v2 对比、包重命名表和 deprecated alias 对照表
- https://orpc.dev/docs/integrations/trpc:增量迁移路径中 和
toORPCRouter的参考文档toTRPCMeta