Ferry代码生成器教程:从GraphQL Schema一键生成不可变强类型类
【免费下载链接】ferryStream-based strongly typed GraphQL client for Dart项目地址: https://gitcode.com/gh_mirrors/fer/ferry
Ferry 代码生成器是 Dart/Flutter 生态中一款强大的 GraphQL 代码生成工具。只需一份 GraphQL Schema 和若干.graphql操作文件,它就能通过一条命令自动为你生成不可变、强类型的 Dart 数据类,让 GraphQL 客户端告别运行时错误。本文是面向新手的完整指南,带你从零跑通Ferry GraphQL 代码生成全流程,并对比两代生成器的选型与进阶配置。
为什么需要 GraphQL 代码生成器?
手写 GraphQL 客户端时,你通常要面对这些痛点:
- ⚠️运行时才报错:字段名拼错、类型不匹配,只有请求发出后才发现
- 🔁手动解析 JSON:反复写
map['field'],空值处理全靠小心翼翼 - 🐌类型安全缺失:IDE 无法补全,重构时无法自动追踪字段变化
Ferry 的解决方案是:基于你的 GraphQL Schema,在编译期生成完整的强类型类。正如 README.md 中所描述的,它是 "Fully Typed" 的核心能力——编译期检查 + IDE 自动补全,连缓存的读写都是强类型的。
快速开始:4步一键生成强类型类
第 1 步:克隆仓库并添加依赖
git clone https://gitcode.com/gh_mirrors/fer/ferry在你的 Dart/Flutter 项目中,将ferry_generator加入dev_dependencies,并安装build_runner(详见 packages/ferry_generator/ 目录说明)。
第 2 步:下载 GraphQL Schema
把服务的 Schema 以 SDL 格式保存到lib/目录下,例如lib/schema.graphql:
npx get-graphql-schema [ENDPOINT_URL] > lib/schema.graphql第 3 步:编写 .graphql 操作文件
把 Query、Mutation 和 Fragment 保存为.graphql文件,必须放在lib/目录内。官方建议放入graphql/子目录,例如仓库中的 examples/pokemon_explorer2/lib/graphql/:
query AllPokemon($first: Int!) { pokemons(first: $first) { id name maxHP } }如果操作引用了其他文件里的 Fragment,用注释导入即可:
# import './pokemon_card_fragment.graphql'第 4 步:配置 build.yaml 并运行构建
在项目根目录创建build.yaml,指向你的 Schema 文件,然后执行:
dart run build_runner build --delete-conflicting-outputs完成后,每个.graphql文件旁边都会出现__generated__目录,强类型类已全部就绪 ✅
生成物详解:generated目录里有什么?
以all_pokemon.graphql为例,生成器会产出以下文件:
| 文件后缀 | 作用 |
|---|---|
*.ast.gql.dart | GraphQL 文档的 AST 常量定义 |
*.data.gql.dart | 响应数据类(强类型模型) |
*.var.gql.dart | 变量类(操作没有变量时不生成) |
*.req.gql.dart | 请求类,继承OperationRequest,内置 FetchPolicy 等执行配置 |
*.schema.gql.dart | Schema 中的枚举、Input 类型、possibleTypes 映射 |
*.utils.gql.dart | 可选的 equals / hashCode 辅助(v2 中按需开启) |
以 v1 生成器为例,AllPokemon查询会生成三个核心类:GAllPokemonReq(请求)、GAllPokemonVars(变量)、GAllPokemonData(响应数据),并自动生成 Schema 中用到的 input、enum 和自定义 scalar 支持类。
💡小知识点:所有类名前都会加
G前缀,这是built_value包的命名限制,也是识别 Ferry 生成类的标志。
两代生成器怎么选?ferry_generator vs ferry_generator2
Ferry 目前提供两代生成器,完整说明见 docs/codegen.md 与 docs/codegen2.md。
v1:ferry_generator(built_value 路线)
生成的类基于built_value包,四大特性:
- 不可变:创建后无法修改
- 可比较:值相同的实例
==相等 - 可序列化:内置
toJson()/fromJson() - Builder 模式:深度复制并修改字段
v1 额外支持多 Schema配置(schemas列表按目录划分作用域),适合复杂的中台架构。
v2:ferry_generator2(下一代,实验性)
ferry_generator2 主打小体积、快构建、纯 Dart 类,核心优势:
- 🪶 无 builder、无序列化器样板代码,输出更小、编译更快
- 🧱
const构造函数 + 直接的toJson/fromJson - 🔒 接口/联集生成sealed 密封类,并附带
__unknown兜底变体,模式匹配更安全 - ⚙️
copyWith、==、hashCode、toString全部可选配置 - 🧩Fragment 复用去重:同一 Fragment 用于多个操作时只映射到一个类
从 v1 迁移到 v2 的详细步骤可参考 docs/migration-generator2.md。
进阶配置:这些长尾选项值得了解
枚举兜底值(防止新枚举值打崩客户端)
服务端新增枚举值而客户端未升级时,可配置 fallback 避免反序列化失败:
ferry_generator|graphql_builder: options: global_enum_fallbacks: true enum_fallbacks: MyEnumType: OTHER自定义标量类型
自定义 scalar(如Date)可在build.yaml中映射为 Dart 类型并指定fromJson/toJson,更多用法见 docs/custom-scalars.md。
三态可选变量(Tristate Optionals)
开启tristate_optionals: true后,可空变量用Value<T>表示"未提供 / 显式 null / 有值"三种状态,专门解决"更新类 Mutation"中区分不更新与置空的经典难题。
输出目录控制
默认输出到__generated__目录;设置output_dir: ''可让生成文件直接落在.graphql旁边。
常见问题 FAQ
Q1:为什么.graphql文件必须在lib/下?A:构建系统只能读取lib/内的资源,放到外面生成器无法访问。
Q2:构建时报 conflicting outputs 怎么办?A:给 build_runner 命令加上--delete-conflicting-outputs参数。
Q3:生成文件太多干扰浏览?A:在 VSCode 的settings.json中用files.exclude隐藏*.ast.gql.dart等模式(docs/codegen.md 提供了现成配置)。
Q4:v2 支持多 Schema 吗?A:目前 v2 仅支持单 Schema(schema.files会直接报错)。多 Schema 场景可拆分为多个 Dart 包、各配一个 Ferry 客户端,或继续使用 v1。
总结
Ferry 代码生成器把 "Schema → 强类型 Dart 类" 变成了一条命令的事:
- 📥4 步上手:下载 Schema、写
.graphql、配置build.yaml、跑 build_runner - 🛡️编译期安全:不可变、可比较、可序列化的强类型模型
- 🆕两代可选:v1 稳定且支持多 Schema;v2 更轻更快,sealed 类型匹配体验极佳
- 📚实战参考:端到端示例见 packages/ferry_generator2_end_to_end/ 与 examples/pokemon_explorer2/
现在就可以克隆仓库,给你的 GraphQL 项目加上 Ferry 强类型代码生成,让错误在编译期就无处遁形!
【免费下载链接】ferryStream-based strongly typed GraphQL client for Dart项目地址: https://gitcode.com/gh_mirrors/fer/ferry
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考