news 2026/10/15 14:50:30

基于 Forge 的 astro-fern 构建 OpenAPI 驱动的 API 文档站:Astro 集成完整指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于 Forge 的 astro-fern 构建 OpenAPI 驱动的 API 文档站:Astro 集成完整指南

【免费下载链接】forge

项目地址:https://gitcode.com/gh_mirrors/forge55/forge
点击查看免费下载

forge是 Cloudflare 开源的 schema-first OpenAPI 工具框架,其中的astro-fern负责把 OpenAPI 规范转换成可直接部署的 API 文档站。本指南面向新手和普通用户,带你完整走一遍:如何用Astro + astro-fern从零搭建一个OpenAPI 驱动的 API 文档站,涵盖目录结构、核心配置、路由策略、AI Agent 友好输出(llms.txt),以及部署注意事项。


📌 先搞清楚:三个角色各管什么

理解分工是入门第一步:数据归astro-fern,展示归你的 Astro 应用。

角色负责内容
astro-fernOpenAPI 加载、快照、代码示例(snippets)、目录(catalog)、路由规划、Agent 文档
fern-forge通过扩展 API 校验 Forge 元数据、提供 SDK 投影
你的 Astro 应用HTML、导航、主题、代码渲染器、可见性策略、自定义 URL

💡 一句话记忆:astro-fern提供归一化的内容和懒加载服务端 API,你的应用只负责把它"画"出来。


🚀 快速上手:最小可用的 5 个核心文件

一个能跑起来的 astro-fern 文档站只需要 5 个文件:

文件作用
astro.config.mjs注册集成、适配器、路由策略、Agent 选项
src/content.config.ts注册apiReference目录,提供内容策略
src/live.config.ts注册apiOperations实时集合
src/env.d.ts为Astro.locals补充类型
src/pages/api/[...slug].astro在配置的 Fern base 下渲染人类操作页面

⚠️ 集合名apiReference与apiOperations是固定的,因为服务端 API 靠它们定位目录和操作工件。

1️⃣ 配置 Astro

在astro.config.mjs中引入astroFern集成(示例使用 Node 适配器):

// astro.config.mjs import node from '@astrojs/node'; import astroFern from 'astro-fern'; import { defineConfig } from 'astro/config'; export default defineConfig({ output: 'server', adapter: node({ mode: 'standalone' }), integrations: [ astroFern({ routing: { base: '/api', target: 'path' }, }), ], });

生产环境请把SITE_URL设为公开部署源;本地开发时,集成会通过Astro.locals自动提供一个 artifact fetcher。

2️⃣ 注册内容(content.config)

在src/content.config.ts里声明产品(products)、目标(targets)和 OpenAPI 源:

// src/content.config.ts import { defineFernManifest, type FernContentOptions } from 'astro-fern'; import { defineFernCollections } from 'astro-fern/collections'; const manifest = defineFernManifest({ targets: [{ id: 'curl', kind: 'http', label: 'curl', language: 'bash' }], products: [ { id: 'dns', pathPrefixes: ['/zones'], sections: [{ id: 'records', tag: 'DNS Records', title: 'DNS records' }], }, ], }); export const collections = { ...defineFernCollections({ source: new URL('./openapi.json', import.meta.url), manifest, } satisfies FernContentOptions), };

把带DNS Records标签的接口放进src/openapi.json,/zones下的操作就自动归到dns产品。

3️⃣ 注册操作加载器(live.config)

// src/live.config.ts import { defineLiveCollection } from 'astro:content'; import { fernOperationLiveLoader } from 'astro-fern/live'; export const collections = { apiOperations: defineLiveCollection({ loader: fernOperationLiveLoader(), }), };

4️⃣ 补充本地类型

// src/env.d.ts /// <reference types="astro/client" /> /// <reference types="astro-fern/locals" />

5️⃣ 渲染一个操作页

--- // src/pages/api/[...slug].astro import { getFernPageForPath } from 'astro-fern/server'; export const prerender = false; const fetcher = Astro.locals.fernArtifactFetcher; const resolved = await getFernPageForPath(Astro.url.pathname, { ...(fetcher ? { request: Astro.request, fetcher } : {}), }); if (!resolved) return new Response('Not found', { status: 404 }); const { page, target } = resolved; --- <main> <h1>{page.operation.title}</h1> <p>{page.operation.httpMethod} {page.operation.path}</p> <div set:html={page.operation.description.html} /> </main>

🧩 关键坑:astro.config 与 content.config 的分工

新手最容易踩的坑是——这两个文件运行在不同的模块图里,职责不能混:

  • 运行时路由选项(routing、agents)→ 写在astro.config
  • OpenAPI 源和内容策略(source、manifest、snippets)→ 写在content.config

真实的 Cloudflare 文档站就是把两者彻底分开维护的,可直接对照 packages/docs-site/astro.config.mjs 与 packages/docs-site/src/content.config.ts。


🗂️ 定义产品与区段(Manifest)

Manifest 为导航和路由提供稳定身份,核心是targets+products:

const manifest = defineFernManifest({ targets: [curlTarget], products: [ { id: 'dns', title: 'DNS', description: 'Manage DNS records and settings.', sdkGroup: 'dns', pathPrefixes: ['/zones'], sections: [ { id: 'records', tag: 'DNS Records', title: 'Records' }, { id: 'settings', tag: 'DNS Settings', title: 'Settings' }, ], }, ], });

产品归属规则(按优先级):

  1. 带sdkGroup的产品,接收第一段 SDK 组相等的操作(优先于pathPrefixes)。
  2. 不带sdkGroup但有pathPrefixes的产品,用路径前缀匹配。
  3. 两者都没有的产品,可接收属于其任一 section 的操作。
  4. 当任意 OpenAPI tag精确匹配section 的tag时,该 section 接收这个操作。
  5. 一个操作可属于多个产品,当且仅当所有匹配产品都声明了sdkGroup。

📎 不想手写产品?discoverProducts()能从 SDK 组首段推导产品 ID、从每个操作的首个 tag 推导 section,一步到位。


🎯 路由策略:hash 还是 path?

模式路径示例适用场景
Hash(默认)/api/dns/records/methods/list/#curltarget 是"交互式状态",一份文档服务所有 target
Path/api/dns/curl/records/methods/list/每个 target 都需要独立可索引的文档(利于 SEO)

非默认快照会在产品后插入 slug,例如:

/api/dns/records/methods/list/ /api/dns/v1/records/methods/list/

📌 选择建议:每个 target 都要被搜索引擎独立收录 → 用path;target 只是页内切换 → 用hash。


🤖 面向 AI Agent:Markdown 与 llms.txt

astro-fern 默认会注入纯 Markdown 操作文档和嵌套的llms.txt索引,让 LLM / AI 助手能高效读取你的 API 文档:

选项默认作用
agents.injectRoutestrue挂载配置的 agent 路由
agents.markdowntrue为每个操作规划聚合 + 每 target 的 Markdown
agents.llms全部作用域规划 site / product / snapshot / target 索引
agents.directive默认指令前置说明文字,设为false可省略

llms.txt各级作用域路径:

作用域路径内容
site/llms.txt所有产品
product/api/{product}/llms.txt默认快照中的产品
snapshot/api/{product}/{slug}/llms.txt一个产品快照
target/api/{product}/targets/{target}/llms.txt一个 target 的操作

💡 想在应用里自己控制挂载路径?把agents.injectRoutes设为false,用getAgentRoutes()拿到所需路径即可。


📚 版本快照(Snapshots)

一个 snapshot 代表一个"被文档化的 API 状态"。单一 source 会创建一个隐式快照(id/slug=current,default: true)。需要多版本时,显式声明:

source: { kind: 'snapshots', snapshots: [ { id: 'v1', label: 'Version 1', slug: 'v1', source: new URL('./openapi.v1.json', import.meta.url) }, { id: 'v2', label: 'Version 2', slug: 'v2', default: true, source: new URL('./openapi.v2.json', import.meta.url) }, ], },

每个快照有自己的 source、身份、label、slug、default标志和可选 target 覆盖。跨版本切换时,直接用内置的无样式组件:

--- import SnapshotSwitcher from 'astro-fern/components/SnapshotSwitcher.astro'; --- <SnapshotSwitcher label="API version" />

它会在至少有一个可用快照时渲染锚点链接,没有适用快照时不渲染任何标记。


📦 部署要点(别漏了)

  • 内容加载会把目录写入public/_astro-fern/operations/与catalogs/,保留该路径并纳入部署静态资源。
  • 用.gitignore忽略public/_astro-fern/。
  • 以 digest 命名的文件给一个长且不可变的缓存(如Cache-Control: public, max-age=31536000, immutable)。
  • 把site设为可信公开源,或向页面加载辅助函数传origin。
  • ⚠️不要把密钥放进 OpenAPI 字段、扩展数据或 snippet 渲染器输出——操作工件是公开可访问的。

🛠️ 本地运行参考

# 克隆仓库并安装依赖 git clone https://gitcode.com/gh_mirrors/forge55/forge cd forge pnpm install # 类型检查 / Lint / 单元测试 pnpm typecheck && pnpm check && pnpm test # 开发 / 构建文档站 pnpm --filter docs-site dev pnpm --filter docs-site build

🔗 延伸阅读与关键源码

  • 完整官方文档:packages/astro-fern/README.md
  • 集成入口:packages/astro-fern/integration.ts
  • Manifest 定义:packages/astro-fern/manifest.ts
  • 内容集合:packages/astro-fern/collections.ts
  • 实时加载器:packages/astro-fern/operation-live-loader.ts
  • 服务端 API:packages/astro-fern/server.ts
  • 真实文档站配置:packages/docs-site/astro.config.mjs
  • 内容注册:packages/docs-site/src/content.config.ts
  • 实时集合:packages/docs-site/src/live.config.ts
  • 操作渲染页:packages/docs-site/src/pages/[product]/[...slug].astro
  • Forge 扩展:packages/fern-forge/README.md
  • 文档站说明:packages/docs-site/README.md

【免费下载链接】forge

项目地址:https://gitcode.com/gh_mirrors/forge55/forge
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/15 14:50:27

SpringBoot游乐园票务系统设计与高并发实践

1. 项目背景与核心价值游乐园作为现代都市休闲娱乐的重要场所&#xff0c;其票务管理系统的智能化程度直接影响运营效率和游客体验。传统的人工售票窗口纸质票模式存在三大痛点&#xff1a;高峰期排队购票时间长、票务数据统计滞后、二次消费转化率低。这个基于SpringBoot的购票…

作者头像 李华
网站建设 2026/10/15 14:50:07

50M Linux虚拟机如何实现?Qume镜像裁剪与QEMU/KVM实战

简介&#xff1a;这是一份小巧实用的 Linux 虚拟机体验包&#xff0c;由 Qume 虚拟机软件与精简版 DSL LiveCD 镜像构成&#xff0c;面向希望在 Windows 98/NT/2000/XP/2003 等老系统中免安装、免重启快速尝试 Linux 的用户。压缩包共 14 个文件&#xff0c;总大小约 48.45MB&a…

作者头像 李华
网站建设 2026/10/15 14:49:45

5G车联网URLLC中基于位置的用户中心资源调度

1. 这个标题到底在解决什么现实问题&#xff1f;——从高速行驶的汽车说起你有没有试过在高速公路上用手机看高清地图导航&#xff0c;突然间路线刷新变慢、语音提示卡顿半秒&#xff0c;或者车载V2X系统在交叉路口未能及时发出碰撞预警&#xff1f;这些看似微小的延迟&#xf…

作者头像 李华
网站建设 2026/10/15 14:49:39

DOS汇编万年历:蔡勒公式+BIOS中断+光标定位实战

简介&#xff1a;本资源是一份面向汇编语言初学者与《微机接口与汇编语言》课程学习者的万年历显示程序实践项目&#xff0c;解决“用底层指令实现日历逻辑与屏幕输出”这一典型教学难点。程序完整实现键盘输入年月日、蔡勒公式计算星期几、闰年判断、光标定位、六日高亮&#…

作者头像 李华
网站建设 2026/10/15 14:49:32

本地AI换脸工具:免费无注册、全离线、无缝融合原理与实战

1. 项目概述&#xff1a;为什么“完全免费还不用注册”的本地FaceSwap突然火了&#xff1f;最近在几个技术群和创意社区里&#xff0c;几乎每天都有人甩出同一句话&#xff1a;“完全免费还不用注册&#xff01;AI FaceSwap本地换脸神器&#xff0c;自动识图无缝融合&#xff0…

作者头像 李华
网站建设 2026/10/15 14:49:15

微电网合作博弈优化:Shapley值在分布式能源调度中的应用

1. 项目背景与核心价值微电网作为分布式能源的重要载体&#xff0c;正在重塑传统电力系统的运行模式。在这个背景下&#xff0c;如何协调光伏、风电、储能、燃气轮机等多元主体的利益诉求&#xff0c;成为当前行业亟需解决的痛点问题。去年参与某工业园区微电网项目时&#xff…

作者头像 李华