Apollo项目中前后端重复定义typeDefs的冗余问题如何优化?
核心认知澄清
首先要明确:你提到的两处定义根本不是同一种东西,不存在“冗余”的前提:
- 服务端
schema.js里的typeDefs是服务端对外暴露的全量接口契约,定义了服务端支持的所有类型、字段、参数、返回值规则,用来做请求校验、解析器绑定、权限校验,是服务端能力的全集。 - 客户端
typeDefs.ts里写的gql语句是当前组件的按需查询声明,只需要声明当前组件用到的字段即可,这是GraphQL相比REST“要什么拿什么”的核心设计,你如果硬要复用服务端全量Schema做查询,反而会每次请求拉取所有无用字段,彻底丢掉GraphQL的优势,还可能引发数据越权问题。
如果你的诉求是减少手动同步Schema的成本、避免客户端查询写错字段、自动生成类型,可以用下面的成熟方案优化:
优化方案1:使用GraphQL代码生成工具自动同步类型与查询封装
这是生产环境的标准实践,完全不需要手动复制服务端Schema到客户端:
- 你的Apollo Server启动后,默认会在
/graphql端点对外暴露标准的SDL格式全量Schema - 引入GraphQL Code Generator工具,直接拉取服务端运行时的Schema,扫描你在客户端写的零散gql查询语句,自动生成:
- 全类型安全的
useQuery/useMutation自定义Hook,不需要你手动封装 - 所有查询参数、返回值的TS类型定义
- 编译阶段自动校验客户端查询的字段是否和服务端Schema匹配,字段名写错、传参不对直接报错,不需要等运行时才发现问题
- 全类型安全的
基础配置示例,安装对应依赖后在项目根目录新建codegen.ts:
import type { CodegenConfig } from '@graphql-codegen/cli'; const config: CodegenConfig = { schema: 'http://localhost:4000/graphql', // 直接读取本地运行的服务端Schema,无需手动复制文件 documents: ['src/**/*.{ts,tsx}'], // 扫描客户端代码里写的gql查询片段 generates: { './src/generated/': { preset: 'client', plugins: [], } } }; export default config;
配置完成后跑生成命令,就可以直接在组件里导入生成的Hook使用,所有类型自动推导,完全不需要手动维护两边的定义同步。
优化方案2:Monorepo架构下抽离公共Schema模块
如果你的前后端代码放在同一个Monorepo仓库中,可以把服务端的Schema定义抽离到独立的公共workspace包中:
- 服务端直接从公共包导入typeDefs,传入ApolloServer完成初始化
- 客户端仅在做本地Mock、客户端缓存Schema配置的时候导入公共Schema,绝对不要直接用全量Schema发起查询
注意:不要直接从服务端代码目录往前端项目里导入文件,否则打包工具会把express、apollo-server-express等Node端依赖打进前端bundle,直接引发打包报错。
现有代码的问题修正
你贴的客户端代码存在一个明显的路径引用错误:MyComponent里导入的路径是../../server/typedefs,但服务端的Schema文件实际名为schema.js,客户端自己写的查询文件是typeDefs.ts,两者不要混淆,直接跨前后端目录导入代码的行为本身就不符合工程规范。
内容的提问来源于stack exchange,提问作者Peter Boomsma
相关产品推荐
相关产品推荐

