You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在不同服务商部署基于tRPC的React与Express应用?

问题描述

我有一个React应用和一个Express应用,两者均已配置使用tRPC。客户端的utils/trpc.ts通过本地相对路径导入AppRouter类型:

import { createTRPCReact } from '@trpc/react-query';
import type { AppRouter } from '../../../chatapp-server/src/api/root';

export const trpc = createTRPCReact<AppRouter>();

已成功将Express应用部署在Render上,但部署React应用到Vercel时失败,报错包含两个核心问题:

  • 客户端通过相对路径查找AppRouter类型,但Vercel构建环境中没有服务器代码,触发TS2307模块找不到错误。
  • 后端router中存在与tRPC内置方法冲突的命名(如useContext、Provider、createClient),导致类型推断错误,出现TS2339属性不存在的报错。

完整报错信息:

src/App.tsx(4,25): error TS2339: Property 'message' does not exist on type '"The property 'useContext' in your router collides with a built-in method, rename this router or procedure on your backend." | "The property 'Provider' in your router collides with a built-in method, rename this router or procedure on your backend." | "The property 'createClient' in your router collides with a built...'.
  Property 'message' does not exist on type '"The property 'useContext' in your router collides with a built-in method, rename this router or procedure on your backend."'.
src/main.tsx(14,8): error TS2339: Property 'createClient' does not exist on type '"The property 'useContext' in your router collides with a built-in method, rename this router or procedure on your backend." | "The property 'Provider' in your router collides with a built-in method, rename this router or procedure on your backend." | "The property 'createClient' in your router collides with a built...'.
  Property 'createClient' does not exist on type '"The property 'useContext' in your router collides with a built-in method, rename this router or procedure on your backend."'.
src/main.tsx(29,11): error TS2339: Property 'Provider' does not exist on type '"The property 'useContext' in your router collides with a built-in method, rename this router or procedure on your backend." | "The property 'Provider' in your router collides with a built-in method, rename this router or procedure on your backend." | "The property 'createClient' in your router collides with a built...'.
  Property 'Provider' does not exist on type '"The property 'useContext' in your router collides with a built-in method, rename this router or procedure on your backend."'.
src/main.tsx(33,12): error TS2339: Property 'Provider' does not exist on type '"The property 'useContext' in your router collides with a built-in method, rename this router or procedure on your backend." | "The property 'Provider' in your router collides with a built-in method, rename this router or procedure on your backend." | "The property 'createClient' in your router collides with a built...'.
  Property 'Provider' does not exist on type '"The property 'useContext' in your router collides with a built-in method, rename this router or procedure on your backend."'.
src/utils/trpc.ts(2,32): error TS2307: Cannot find module '../../../chatapp-server/src/api/root' or its corresponding type declarations.
 ELIFECYCLE  Command failed with exit code 2.
Error: Command "pnpm run build" exited with 1
BUILD_UTILS_SPAWN_1: Command "pnpm run build" exited with 1

涉及的客户端代码:
App.tsx:

import { trpc } from "./utils/trpc"

function App() {
  const { data } = trpc.message.getAll.useQuery();
  console.log(data);
  return (
    <>
      <h1> asd </h1>
    </>
  )
}

export default App

main.tsx:

import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'
import './index.css'

import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { httpBatchLink } from '@trpc/client';
import { trpc } from './utils/trpc';
import superjson from 'superjson';

const queryClient = new QueryClient();
const trpcClient = 
  trpc.createClient({
    transformer: superjson,
    links: [
      httpBatchLink({
        url: import.meta.env.VITE_SERVER_URL || '',
        async headers() {
          return {}
        },
      }),
    ],
  })

ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
  <React.StrictMode>
    <trpc.Provider client={trpcClient} queryClient={queryClient}>
      <QueryClientProvider client={queryClient}>
        <App />
      </QueryClientProvider>
    </trpc.Provider>
  </React.StrictMode>,
)
可行性说明

完全可行,tRPC原生支持前后端分离部署,核心是解决类型共享和命名冲突两个问题。

解决办法

1. 修复命名冲突问题

报错明确指出,后端router中存在与tRPC客户端内置方法重名的路由/过程(useContext、Provider、createClient),这些名称是tRPC客户端的保留属性,不能用于后端定义。

操作步骤:

  • 打开后端Express应用的路由定义文件(如src/api/root.ts)。
  • 查找所有名为useContext、Provider、createClient的路由或过程,将其重命名为非保留名称:
    • 示例:useContext改为getUserContext,Provider改为AppServiceProvider,createClient改为createAppClient。
  • 重启后端服务,确认路由正常运行。

2. 解决类型共享问题

本地开发时用相对路径导入类型可行,但Vercel构建环境无服务器代码,必须更换类型共享方式,以下是三种常用方案:

方案一:使用Monorepo结构(推荐)

将React客户端和Express服务器放在同一仓库,通过Monorepo工具(如pnpm workspaces、TurboRepo)共享类型目录,确保Vercel构建时能访问到类型文件。

操作步骤:

  1. 调整仓库结构:
your-project/
├── packages/
│   ├── client/          # React应用
│   ├── server/          # Express应用
│   └── trpc-types/      # 共享类型目录
  1. 在server/src/api/root.ts中导出AppRouter,并将类型定义同步到trpc-types目录(或直接在trpc-types中定义路由类型)。
  2. 在客户端package.json中添加共享类型依赖:
{
  "dependencies": {
    "trpc-types": "workspace:*"
  }
}
  1. 修改客户端utils/trpc.ts的导入路径:
import { createTRPCReact } from '@trpc/react-query';
import type { AppRouter } from 'trpc-types';

export const trpc = createTRPCReact<AppRouter>();
  1. 配置Vercel构建:在项目设置中将Root Directory设为packages/client,确保Monorepo依赖解析正常。

方案二:将类型发布为私有npm包

把tRPC的AppRouter类型单独抽取为npm包,发布到私有仓库(如npm私有包、GitHub Packages),再在客户端和服务器中安装该包。

操作步骤:

  1. 创建新npm包项目,导出AppRouter类型:
// trpc-types/src/index.ts
import type { AppRouter } from '../path/to/server/src/api/root';
export type { AppRouter };
  1. 将包发布到私有npm仓库。
  2. 在客户端和服务器的package.json中安装该包:
pnpm install trpc-types
  1. 修改客户端utils/trpc.ts的导入路径为npm包:
import { createTRPCReact } from '@trpc/react-query';
import type { AppRouter } from 'trpc-types';

export const trpc = createTRPCReact<AppRouter>();

方案三:构建时复制类型文件到客户端

在客户端构建脚本中,先从服务器复制类型文件到客户端目录,再执行构建。

操作步骤:

  1. 在客户端package.json中添加预处理脚本:
{
  "scripts": {
    "prebuild": "cp ../chatapp-server/src/api/root.ts src/types/trpc-root.ts",
    "build": "vite build"
  }
}

(若服务器在远程,可在后端添加接口返回类型文件内容,用curl命令拉取)
2. 修改客户端utils/trpc.ts的导入路径:

import { createTRPCReact } from '@trpc/react-query';
import type { AppRouter } from './types/trpc-root';

export const trpc = createTRPCReact<AppRouter>();
验证部署

完成修改后:

  1. 重新部署Express服务器到Render,确保路由命名修改生效。
  2. 部署React应用到Vercel,检查构建是否成功。
  3. 访问Vercel上的React应用,验证tRPC请求是否正常发送到Render服务器。

内容的提问来源于stack exchange,提问作者Timotei Oros

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 02:47:01