Fumadocs GraphQL:从 GraphQL Schema 一键生成带交互式 Playground 的 API 参考文档
【免费下载链接】fumadocsThe beautiful & flexible React.js docs framework.项目地址: https://gitcode.com/GitHub_Trending/fu/fumadocs
导读
@fumadocs/graphql是 Fumadocs 生态中专用于 API 参考文档生成的官方包:它读取你的 GraphQL Schema(SDL 文件、introspection JSON 或GraphQLSchema实例),将其转换成一批可直接挂载到 Fumadocs 内容源(source)中的虚拟页面,并为每个 Operation 页面内置交互式 Playground,支持类型与操作间的交叉链接、弃用标记和深度的 UI 自定义。阅读本文后,你将掌握安装、服务端接入、页面生成、客户端渲染、样式引入的完整流程,并能理解per页面预设、groupBy分组、baseUrl链接预生成等核心参数背后的源码实现,从而把 GraphQL Schema 快速变成一套专业、可检索、可联调的技术文档。
一、包结构与安装
@fumadocs/graphql位于仓库的 packages/graphql 目录,当前版本0.2.6。从 package.json 的exports字段可以看到它对外暴露了四个入口,分别承担不同职责:
@fumadocs/graphql/server:服务端入口(dist/server/index.js),提供createGraphQL()工厂函数,负责加载 Schema 并把 Schema 转换成虚拟页面;@fumadocs/graphql/ui:客户端入口(dist/ui/index.js),提供createGraphQLPage(),负责把页面数据渲染成可视化文档,包含 Schema 视图与 Playground;@fumadocs/graphql/css/*:预设样式;@fumadocs/graphql根入口:重导出executeGraphQL、页面构建相关类型等通用内容。
安装命令与 README 一致,注意graphql是 peer dependency,需要自行安装,且要求graphql@^17.0.0及以上(同时 peer 依赖fumadocs-core/fumadocs-ui^16.15.0、React 19):
npm i @fumadocs/graphql graphql包本身还依赖@fumadocs/api-docs(复用其 Schema 视图/类型注解等能力)、shiki(代码高亮)、github-slugger等,这些会在构建时自动安装,无需手动处理。
二、服务端接入:创建 GraphQL 服务实例
先在lib/graphql.ts中用 createGraphQL 创建服务端实例。input支持两种形态:
- 字符串数组:一组 SDL 文件路径或 URL;
- Schema 记录(record):
schema id -> input的映射,适合多 Schema 场景。
// lib/graphql.ts import { createGraphQL } from '@fumadocs/graphql/server'; export const graphql = createGraphQL({ input: ['./schema.graphql'], });从源码看(server/index.tsx),createGraphQL还接受disableCache选项,默认false即开启内部Map缓存:同一个 schema id 的加载结果(Promise<LoadedSchema>)会被缓存,避免重复解析;若 Schema 会在运行时变化(如动态获取),可传入disableCache: true关闭缓存。
2.1 input 支持的四种输入形态
依据 load-schema.ts 的实现,input中每一项可以是:
| 输入形态 | 说明 | 底层处理 |
|---|---|---|
| SDL 文件路径 / URL | 以.graphql/.graphqls/.gql结尾的本地路径,或http(s)://远程地址;也支持字符串数组(合并为单个 Schema) | 读取文件或fetch远程内容后,通过buildSchemaFromSDL构建,并用validateSchema做严格校验 |
| SDL 文本 | 直接内联的 Schema 字符串,可包含extend type定义 | 同上,作为 SDL 直接构建 |
| Introspection 结果(JSON) | IntrospectionQuery或{ data: IntrospectionQuery }结构 | 通过buildClientSchema还原为可执行的GraphQLSchema |
GraphQLSchema实例 | 代码中已构建好的 Schema 对象 | 直接使用,并通过printSchema生成 SDL 文本 |
注意loadSchema对 SDL 输入会执行validateSchema严格校验,出错时会抛出带具体错误信息的异常;而客户端侧的buildSchemaFromSDL会跳过校验,因此服务端是 Schema 合法性的第一道关口。加载成功后返回LoadedSchema,其中sdl字段是 Schema 的 SDL 文本形式,会被传递到客户端用于重建 Schema 与渲染。
三、把生成的页面接入内容源
在lib/source.ts中,把graphql.staticSource()的结果作为一个独立 source 传入loader(),并通过plugins注册loaderPlugin():
// lib/source.ts import { loader } from 'fumadocs-core/source'; import { defineDocs } from 'fumadocs-mdx/macro'; import { graphql } from './graphql'; const docs = defineDocs({ dir: 'content/docs', }); export const source = loader( { docs: docs.toFumadocsSource(), graphql: await graphql.staticSource({ // a route group, generated pages won't have a `/graphql` prefix in their URLs baseDir: '(graphql)', meta: true, }), }, { baseUrl: '/docs', plugins: [graphql.loaderPlugin()], }, );这里的staticSource会把 Schema 展开为一组虚拟.mdx页面(与常规content/docs下的 MDX 页面共存)。从源码实现看,createGraphQL返回的服务实例提供了四种能力(server/index.tsx):
staticSource():构建时一次性生成静态页面;dynamicSource():提供cache: 'custom'的动态源,支持按需重新生成并带invalidate()清理缓存,适合 Schema 频繁变化的场景;loaderPlugin():返回页面树转换插件,负责在侧边栏/页面树中渲染弃用删除线(line-through)与 query/mutation/subscription 操作徽标;getSchema(schemaId)/getSchemas():按 id 获取已加载的 Schema,供 API 路由等场景使用。
staticSource还接受baseDir(路由分组目录,例如(graphql)可让生成的页面 URL 不带/graphql前缀)与meta(是否自动生成meta.json导航元数据,见下节)。
loaderPlugin()对应的 graphqlPlugin 实现值得关注:它以enforce: 'pre'在页面树转换前执行,对带_graphql元数据的页面做两件事——若deprecated为 true 则在页面树节点名上添加删除线样式;若页面属于 query/mutation/subscription 三种操作之一,则在节点名后追加对应的KindLabel徽标。
3.1 meta.json 自动生成
当meta: true时,服务端会根据页面分组结构自动生成meta.json文件(server/index.tsx)。meta选项还支持对象形式{ folderStyle: 'folder' | 'separator' }:
folder(默认):分组以真实文件夹形式呈现,meta.json的pages数组记录相对路径;separator:分组渲染为分隔符(---标题---)加展开项(...相对路径),适用于想用分隔线而非文件夹组织导航的布局。
每个meta.json会带上所属分组的title与description。
四、页面预设(Page Presets)
staticSource()/dynamicSource()内部会把 Schema 转成页面树,这一逻辑集中在 schemaToPages。per选项决定页面的组织粒度,共三种预设:
4.1 per: 'item'(默认)
每个 Operation 或具名类型生成一页。该模式接受以下参数:
groupBy: 'kind' | 'none' | function:默认'kind',按类型分组到queries/、mutations/、subscriptions/、objects/、interfaces/、unions/、enums/、inputs/、scalars/九个目录(分组目录与标题的映射见 pages.ts 中的 KindGroups);'none'则全部平铺;也可以传函数,按返回值自定义分组目录;includeOperations: boolean | (kind, field) => boolean:默认true,是否(以及按条件)为根类型上的每个操作字段生成页面;includeTypes: boolean | (type) => boolean:默认true,是否(以及按条件)为每个具名类型生成页面;name: (output) => string:自定义页面文件名;slugify: (name) => string:把名称转换为 URL 安全的片段。源码注释明确说明 GraphQL 名称本身已是 URL 安全的,因此默认保留原名(含大小写),只有自定义groupBy函数的分组名会经过slugify。
4.2 per: 'file'
每个 Schema 只生成一个汇总页面,页面中列出该 Schema 的所有操作与类型(类型为page的items数组)。页面名称取自 Schema 文件名(远程输入则回退为index或Overview),可用name覆盖。
4.3 per: 'custom'
完全自建页面构建器:传入toPages(builder)回调,通过builder.create(entry)手动生成任意OutputEntry(操作页、类型页、汇总页或分组),适合需要特殊页面结构的场景。
includeOperations/includeTypes也可以传函数做精细化过滤,例如只对某类操作生成页面;页面标题方面,操作页标题由字段名生成(getOperationTitle),描述取字段/类型的description,deprecated状态由deprecationReason推导。
五、客户端渲染与 UI 配置
5.1 创建 GraphQL 页面组件
在客户端组件中调用 createGraphQLPage:
// components/api-page.tsx 'use client'; import { createGraphQLPage } from '@fumadocs/graphql/ui'; export const GraphQLPage = createGraphQLPage({ playground: { url: '/api/graphql', }, });5.2 接入文档页面
在动态路由页面中,根据page.type === 'graphql'分支渲染(README 示例为app/docs/[[...slug]]/page.tsx,examples/graphql示例中对应的路由为 examples/graphql/app/docs/[[...slug]]/page.tsx):
if (page.type === 'graphql') { return ( <DocsPage toc={page.data.toc} full> <DocsTitle>{page.data.title}</DocsTitle> <DocsDescription>{page.data.description}</DocsDescription> <DocsBody> <GraphQLPage {...page.data.getGraphQLPageProps()} /> </DocsBody> </DocsPage> ); }getGraphQLPageProps()由服务端在生成虚拟页面时注入(server/index.tsx),返回的数据结构为GraphQLPageProps:包含payload.sdl(Schema 的 SDL 文本,客户端据此用buildSchemaFromSDL重建 Schema)与payload.links(类型/操作到页面 URL 的映射),以及items等页面渲染信息。
渲染流程上,GraphQLPage组件会构建一个RenderContext(封装 Schema、SDL、shiki、链接与各类渲染插槽),操作条目交给Operation组件、类型条目交给TypeDocs组件渲染(见 api-page.tsx)。
5.3 引入样式
在全局 CSS 中引入预设样式:
@import '@fumadocs/graphql/css/preset.css';样式文件位于 packages/graphql/css/preset.css。
5.4 createGraphQLPage 的完整选项
从CreateGraphQLPageOptions类型定义(ui/index.tsx)可看到完整的自定义能力:
typeLinks?: (name, ctx) => string | undefined:解析某个具名类型文档页的 URL,用于类型引用间的交叉链接;返回undefined表示该类型没有独立页面;operationLinks?: (kind, name, ctx) => string | undefined:解析某个操作文档页的 URL,用于类型页上的“使用该操作的场景(usage backlinks)”反向链接;playground:交互式 Playground,见下一节;shiki/shikiOptions:代码高亮工厂与主题配置,默认defaultShikiFactory、浅色github-light/ 深色github-dark;components:覆盖内部渲染组件(Heading、CodeBlock、Markdown);content:布局插槽——renderPageLayout(页面级)、renderOperationLayout(操作页:header、description、deprecated、directives、playground、arguments、returns、example 等插槽)、renderTypeLayout(类型页:header、description、directives、relations、fields、values、scalar 等插槽);schemaUI.render:替换整个 Schema 视图。
六、Playground 与交叉链接
6.1 Playground 配置
playground选项控制操作页面上的交互式 Playground,支持三种配置方式:
url: string:GraphQL 端点地址,操作通过 HTTP POST 发送。默认请求头为Content-Type: application/json与Accept: application/graphql-response+json, application/json,请求体为{ query, variables }(见 fetcher.ts);fetcher?: (request, ctx) => Promise<PlaygroundResult>:替换默认 fetcher,例如通过代理转发请求、附加鉴权等;render?: (context) => ReactNode:完全替换 Playground UI。
另外还有allowUrlEdit(默认true,允许用户编辑端点 URL,关闭后以纯文本渲染)与headers(默认请求头,作为端点无已存 header 时的初始行)。
PlaygroundRequest包含url、query、variables、headers;PlaygroundResult区分response(含status、耗时time、body、contentType)与client_error(含message)。默认 fetcherexecuteGraphQL同时被包根入口导出,可在服务端/客户端复用。
6.2 交叉链接的预生成
README 提到:当你在staticSource()中传入baseUrl(即loader()的baseUrl,如/docs)时,类型与操作的交叉链接会被预先生成。实现上,链接生成发生在 generateLinks:遍历loader.getPages(locale)中所有带_graphql元数据的页面,把“类型名 -> 页面 URL”存入links.types,把“${kind}:${name}-> 页面 URL”存入links.operations,最终作为payload.links传入客户端。如果你有特殊的分组或命名需求,可以用createGraphQLPage的typeLinks/operationLinks回调覆盖默认链接解析逻辑。
七、完整示例:examples/graphql
仓库提供了开箱即用的完整示例 examples/graphql,可作为最佳实践模板:
- Schema:examples/graphql/schema.graphql;
- 服务端实例:examples/graphql/lib/graphql.ts;
- 内容源接入:examples/graphql/lib/source.ts(
baseDir: '(graphql)'+meta: true,与上文一致); - 页面渲染分支:examples/graphql/app/docs/[[...slug]]/page.tsx;
- Playground 端点:examples/graphql/app/api/graphql/route.ts。
值得一提的是示例中的 Playground API 路由:它是一个返回示例数据的 mock GraphQL 端点(route.ts),演示了如何为 Playground 提供可联调的端到端体验。该实现包含三项值得参考的工程实践:
- 请求体大小限制:
MaxBodySize = 100 * 1024,超过返回 413; - 查询深度限制:自定义
depthLimit(15)校验规则,防止深层嵌套文档耗尽执行资源; - 示例值 fieldResolver:对真实 Schema 执行
execute(),通过自定义 resolver 为各类型返回示例值(如ID->'1'、String->'string'、枚举取首个值、列表返回单元素数组等),使文档中的示例查询能立即得到可视化响应。
将真实 GraphQL 服务器接入时,只需把playground.url指向实际端点,或保留/api/graphql路由并把内部实现替换为对上游服务器的代理转发。
八、从源码看工作流程
把以上内容串起来,@fumadocs/graphql的完整工作流程如下:
- 加载 Schema:
createGraphQL({ input })注册输入源;loadSchema依据输入形态(SDL/URL/introspection/GraphQLSchema)构建GraphQLSchema并生成 SDL 文本,同时进行缓存与校验; - 生成页面树:
staticSource()/dynamicSource()调用schemaToPages,按per预设把 Schema 转为OperationOutput/TypeOutput/PageOutput/OutputGroup组成的输出树,每个节点附带path、标题、描述、弃用状态等元信息; - 产出虚拟文件:
getVirtualFiles把输出树映射为虚拟.mdx页面(可选自动生成meta.json),并为每页注入getGraphQLPageProps()、getSchema()、toc、structuredData、_graphql元数据; - 链接预生成:页面树配置完成后,
generateLinks扫描全部_graphql页面,产出{ types, operations }链接表随payload.links下发; - 页面树增强:
loaderPlugin()在页面树中为弃用项加删除线、为操作项加类型徽标; - 客户端渲染:
createGraphQLPage用 SDL 在客户端重建 Schema,通过Operation/TypeDocs/ Schema 视图渲染,并依据payload.links生成类型与操作间的交叉链接;操作页上的 Playground 通过url/fetcher/render三种方式接入端点并执行查询。
九、总结
@fumadocs/graphql把「Schema 解析 → 文档页面生成 → 侧边栏导航 → 交互式 Playground → 类型间交叉链接」整条链路封装在一个包内:服务端用createGraphQL+staticSource/dynamicSource生成虚拟页面,客户端用createGraphQLPage渲染,配套preset.css与loaderPlugin()完成样式与导航增强。无论是快速为单个 Schema 生成完整 API 参考,还是通过per: 'custom'与各类渲染插槽构建高度定制化的文档体验,都能在保留 Fumadocs 原生文档能力(TOC、全文搜索、MDX 混排)的前提下直接获得。
进一步探索的仓库路径:
- 服务端实现:packages/graphql/src/server/index.tsx
- 页面生成逻辑:packages/graphql/src/utils/pages.ts
- Schema 加载:packages/graphql/src/utils/load-schema.ts
- 客户端 UI:packages/graphql/src/ui/index.tsx
- Playground fetcher:packages/graphql/src/playground/fetcher.ts
- 完整示例:examples/graphql
【免费下载链接】fumadocsThe beautiful & flexible React.js docs framework.项目地址: https://gitcode.com/GitHub_Trending/fu/fumadocs
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考