先记住这个答案
GraphQL schema 用类型系统完整描述 API 能返回什么数据。核心是六种命名类型:Object 类型定义字段结构,Scalar 是叶子值如 String、Int、Boolean、ID、Float,Enum 枚举有限取值,Interface 定义公共字段契约,Union 表示几种类型之一,Input Object 专用于参数输入。字段可带参数,类型可用 ! 和 [] 修饰表示非空与列表。Query、Mutation、Subscription 是普通 Object 类型,只是被声明为查询、变更、订阅三个操作的入口。schema 可以用语言无关的 SDL 书写,请求到达后按 schema 校验并逐字段执行。
- 六种命名类型加两种修饰符构成 schema
- 根类型只是被指定为入口的普通 Object
- Scalar 是查询叶子,Object 才可继续选择
类型系统如何把 API 能力描述成一张契约
schema 的本质是一份可校验的契约:服务端用它声明客户端能问什么,客户端据此预测响应形状。Object 类型是主体,每个字段标注输出类型,字段还可以声明参数,参数只能用输入类型。Scalar 是查询的终点,字段解析到标量就不能再向下选择;内置 String、Int、Float、Boolean、ID 之外可以自定义标量。
抽象类型解决多态:Interface 要求实现类型提供全部公共字段,查询可直接选公共字段,选特有字段需行内片段;Union 不共享字段,只声明可能是哪几种 Object。类型默认可空且单值,! 表示非空承诺,[] 表示列表,两者可组合成 [User!]! 这类精确语义。Enum 把字段取值收敛到有限集合,便于校验与文档化。
电商商品与搜索结果的建模决策
假设为电商 API 建模:商品有 id、name、price,图书额外有 author,家电额外有 warrantyMonths,搜索接口返回图书或家电。约束是前端搜索页只展示名称和价格,详情页才按类型渲染。决策:定义 interface Product { id: ID! name: String! price: Int! },Book 与 Appliance 实现它,search(keyword: String!): [Product!]! 返回接口列表。
结果:搜索页查询公共字段不需要类型分支,详情页用 ... on Book 选择特有字段。若改用 union SearchResult = Book | Appliance,搜索页连 name 都必须写行内片段,冗余且易错。这里的关键判断是:候选类型之间存在客户端稳定依赖的公共字段时选 Interface,否则才用 Union,字段非空标记则按数据库约束如实标注。
类型声明在哪些情况下会失效或误导
schema 只承诺形状不承诺实现质量:price: Int! 声明非空,但若 resolver 因下游故障返回 null,该字段错误会沿父级向上冒泡,可能把整个对象置空。类型系统也无法表达业务规则,比如“折扣价必须低于原价”仍需参数校验或服务层逻辑。把 SDL 当作完整真相是常见误判。
处理方式:对可能失败的字段避免滥用 !,让部分失败只影响局部而非整棵树;业务不变量写在 resolver 或服务层,schema 仅做结构校验。代价是非空减少后客户端必须处理更多 null 分支,TypeScript 生成类型出现更多可空字段。判断标准:该字段的值是否在所有故障路径下都能保证存在,不能就保持可空。
容易答错的地方
- 认为 Query 和 Mutation 是特殊语法类型
- 根操作类型本质是普通 Object 类型,只是通过 schema 声明被指定为入口。其字段的解析、参数、非空规则与任何 Object 字段完全一致,也可以用
schema { query: MyQuery }改名。 - 把 Input Object 与 Object 混用
- 输出用的 Object 类型不能直接作参数类型。参数必须用 Input Object 或标量、Enum,因为入参不允许循环引用且不需要接口、联合等多态能力,混用会在 schema 构建期直接报错。
面试官还会怎么问?
Interface 和 Union 怎么选?
候选类型有客户端需要直接访问的公共字段时用 Interface,公共字段可写在接口选择上;仅表示若干不相关类型之一时用 Union。Union 成员必须是 Object 类型,且选择任何字段都要行内片段。
自定义标量(如 DateTime)属于哪部分?
属于 Scalar 类型扩展,需要在 schema 中声明 scalar DateTime 并在实现侧提供序列化、解析值、解析变量字面量三个函数。它仍是查询叶子,不能携带子字段。
SDL 与 code-first 定义 schema 有差别吗?
产物等价,都是同一份可内省的 schema。SDL 语言无关、便于评审契约;code-first 用宿主语言构造类型,便于与代码同步。区别在开发流程而非类型系统能力。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。