- 后端
- 前端
- Web框架
- 开发工具
【免费下载链接】redwood
RedwoodGraphQL
导读
本文围绕 RedwoodJS 中 GraphQL Schema 默认注入的File标量展开,说明它从 v8.0.0 起被自动加入 schema 所带来的命名冲突问题,以及通过 changeset .changesets/11540.md 引入的可关闭机制。读完本文你将掌握:如何在redwood.toml与api/src/functions/graphql.ts两处协同配置、该开关在源码中的完整生效链路(从项目配置解析到 schema 组装再到类型生成),以及禁用后自定义File标量或移除它的注意事项。
背景:v8.0.0 起默认注入的File标量
RedwoodJS 从 v8.0.0 开始,会在生成的 GraphQL schema 中默认加入一个File标量。这样做的好处是开箱即用地支持文件上传等场景;但副作用是:如果应用想要自己定义同名File标量,就会与框架默认注入的标量发生命名冲突,导致 schema 合并失败或语义被覆盖。
changeset .changesets/11540.md 正是为解决这一问题而引入:它允许开发者关闭默认注入File标量的行为,把 schema 的标量空间完全交还给应用。
从源码可以看到,框架的默认标量分为两类:
- 不可关闭的通用标量:
BigInt、Date、Time、DateTime、JSON、JSONObject、Byte,定义在 packages/graphql-server/src/rootSchema.ts 的 root schema 中,始终注入; - 可配置的扩展标量:目前仅有
File,单独抽离在scalarSchemas对象中(见 packages/graphql-server/src/rootSchema.ts),其注入与否由配置开关控制。
// packages/graphql-server/src/rootSchema.ts export const scalarSchemas = { File: gql` scalar File `, } export type ScalarSchemaKeys = keyof typeof scalarSchemas这种设计使得框架内置标量分为「always-on」与「opt-out」两种治理模型,为后续扩展更多可配置标量预留了结构。
配置方式一:redwood.toml中的graphql.includeScalars
关闭默认File标量需要在两个地方同时配置。首先是在项目根目录的redwood.toml中:
[graphql] includeScalars.File = false配置项的类型与默认值
该配置项在 packages/project-config/src/config.ts 中被建模为graphql.includeScalars: { File: boolean },其默认值是{ File: true }(见 packages/project-config/src/config.ts):
graphql: { fragments: false, trustedDocuments: false, includeScalars: { File: true }, },也就是说:不写任何配置时,File标量默认包含;只有显式设置为false才会排除。这一「默认包含、显式排除」的语义同样被记录在GraphQLYogaOptions.includeScalars的类型注释中(见 packages/graphql-server/src/types.ts):
The default is to include. You must set to
falseto exclude.
在运行时,getConfig()会解析redwood.toml并合并上述默认值,最终得到完整的graphql.includeScalars结构。配置测试(packages/project-config/src/tests/config.test.ts)也覆盖了该字段的解析行为。
配置方式二:api/src/functions/graphql.ts中的includeScalars选项
第二处配置位于 API 侧的 GraphQL 处理器入口api/src/functions/graphql.ts。在该文件的createGraphQLHandler调用中传入includeScalars选项:
export const handler = createGraphQLHandler({ authDecoder, getCurrentUser, loggerConfig: { logger, options: {} }, directives, sdls, services, onException: () => { // Disconnect from your database with an unhandled exception. db.$disconnect() }, // highlight-start includeScalars: { File: false, }, // highlight-end })为什么两处都要配置?
redwood.toml中的配置服务于构建期与代码生成期:SDK 需要据此判断是否把File标量写入生成的 schema 与类型定义(详见下文「生成链路」);graphql.ts中的配置服务于运行时 schema 组装:createGraphQLHandler会把该值一路透传给makeMergedSchema,决定最终暴露给客户端的 schema 中是否包含File标量。
两处不一致会导致「生成的类型定义里有File、但运行时 schema 没有」或反之的漂移,因此官方文档明确要求两处同步配置。includeScalars选项的类型为RedwoodScalarConfig(见 packages/graphql-server/src/types.ts):
export interface RedwoodScalarConfig { File?: boolean }其注释同样提醒:该选项应与redwood.toml中的graphql.includeScalars保持一致。
底层原理:File标量注入的完整链路
1. 运行时 schema 组装:makeMergedSchema
运行时是否注入File标量,最终由 packages/graphql-server/src/makeMergedSchema.ts 决定。核心逻辑如下:
export const makeMergedSchema = ({ sdls, services, schemaOptions = {}, directives, subscriptions = [], includeScalars, }: { // ... includeScalars?: RedwoodScalarConfig // ... }) => { const sdlSchemas = Object.values(sdls).map(({ schema }) => schema) const rootEntries = [rootGqlSchema.schema] // We cannot access the getConfig from project-config here so the user must supply it via a config option if (includeScalars?.File !== false) { rootEntries.push(rootGqlSchema.scalarSchemas.File) } const typeDefs = mergeTypes([ ...rootEntries, ...directives.map((directive) => directive.schema), ...subscriptions.map((subscription) => subscription.schema), ...sdlSchemas, ], { all: true }) // ... }关键点:
includeScalars?.File !== false的判定意味着只有显式传入false才排除,undefined或true都会保留File标量;- 源码注释明确指出「无法在此处访问 project-config 的 getConfig,因此必须由调用方通过配置选项传入」——这正是要求你在
graphql.ts中手写includeScalars的原因; makeMergedSchema的调用链为createGraphQLHandler→createGraphQLYoga(packages/graphql-server/src/createGraphQLYoga.ts)→makeMergedSchema,includeScalars在每一层都被透传。
2. 生成期链路:schema 文件与 TS 类型
redwood.toml中的开关同时影响yarn rw generate types等命令产出的文件:
- schema 生成:packages/internal/src/generate/graphqlSchema.ts 遍历
rootSchema.scalarSchemas,仅当redwoodProjectConfig.graphql.includeScalars[name]为真时才把该标量加入 schema 指针映射:
for (const [name, schema] of Object.entries(rootSchema.scalarSchemas)) { if (redwoodProjectConfig.graphql.includeScalars[name as ScalarSchemaKeys]) { schemaPointerMap[print(schema)] = {} } }- TS 类型生成:packages/internal/src/generate/graphqlCodeGen.ts 中,只有
config.graphql.includeScalars.File为真时,才会把File映射为File类型,否则不生成对应映射:
const config = getConfig() if (config.graphql.includeScalars.File) { scalars.File = 'File' }对应的快照测试(packages/internal/src/tests/snapshots/graphqlSchema.test.ts.snap)确认默认情况下生成的 schema 中包含scalar File;配置测试(packages/internal/src/tests/clientPreset.test.ts)则以includeScalars: { File: true }为前提验证客户端预设的生成行为。
3. 关闭后会发生什么
当两处配置都设为File: false后:
- 运行时合并的 schema 中不再包含
scalar File定义,应用可以自由地在自己的 SDL 中声明同名File标量(或依赖其他库提供),不会再与框架默认标量冲突; - 生成的
schema.graphql与 TS 类型定义中同样不再出现File标量,保证开发期类型与运行时行为一致; - 若应用既不自定义
File标量、又关闭了默认注入,则在 SDL 中引用File会导致 schema 校验失败——关闭操作应当与「自行定义或继续不使用」二选一配套进行。
自定义File标量:冲突解决后的落地方式
关闭默认注入后,即可按 RedwoodJS 自定义标量的常规流程注册自己的File标量。相关文档位于 docs/docs/graphql.md 的Custom Scalars一节(含Scalars vs Service vs Directives的选型对比)。基本思路是:
- 在
api/src下定义 SDL 与对应的 resolver,声明scalar File; - 通过
createGraphQLHandler的schemaOptions或 SDL 配套 resolver 注册其序列化/反序列化逻辑; - 保持
redwood.toml与graphql.ts两处includeScalars.File = false的同步配置。
提示:默认注入的
File标量仅提供scalar File的类型声明(见rootSchema.ts的scalarSchemas),其语义对应标准的File类型。自定义实现时需自行补齐解析、序列化与校验逻辑。
小结
File标量自 v8.0.0 起默认注入 schema,可能导致与自定义File标量命名冲突;- 关闭方式为「双配置」:
redwood.toml的[graphql] includeScalars.File = false+graphql.ts的includeScalars: { File: false }; - 默认值为
{ File: true },只有显式false才排除;运行时判定逻辑为includeScalars?.File !== false(makeMergedSchema.ts); - 该开关同时作用于运行时 schema 组装、生成期 schema 文件与 TS 类型三个层面,两处配置必须保持一致,否则会出现类型与运行时 schema 的漂移;
- 关闭后即可自由自定义
File标量,或直接移除该类型。
如果你想深入了解 RedwoodJS 其他内置标量(DateTime、JSON等的映射行为)或自定义标量的完整写法,可继续阅读 docs/docs/graphql.md 的Custom Scalars章节,以及 packages/graphql-server/src/rootSchema.ts 中的默认标量声明。
- 后端
- 前端
- Web框架
- 开发工具
【免费下载链接】redwood
RedwoodGraphQL
相关推荐
illustrator-scripts开发者指南:从使用到二次开发
illustrator scripts开发者指南:从使用到二次开发 illustrator scripts 是一套强大的 Adobe Illustrator 脚
开发工具图像处理Voicebox快速上手清单:从克隆声音到生成第一条AI语音的7个步骤
Voicebox快速上手清单:从克隆声音到生成第一条AI语音的7个步骤 Voicebox 是一个免费开源的 AI 语音工作室(AI voice studio),
人工智能语音音频桌面应用本地部署MCP 服务gs-quant 从零到一:卡尔曼滤波价差套利实战
gs quant 从零到一:卡尔曼滤波价差套利实战 2023 03 07,铁矿石单日暴涨 4%,螺纹钢 铁矿石的固定布林带价差策略直接打穿止损。把均值换成动态估
金融科技数据分析
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考