Blume中文文档

API 参考

OpenAPI 参考

把 OpenAPI 规范渲染成原生 API 参考,每个接口一个真实页面,带 schema 表、代码示例和 Try it 演练场

把一个 OpenAPI 规范指给 Blume,它就会生成一份原生 API 参考:每个接口一个真实页面,按 tag 分组放进受标签页约束的侧边栏,带 schema 表、请求/响应示例、生成的代码示例,以及一个交互式 Try it 面板。由于每个接口都是一个真正的 Blume 页面,它有自己的 URL,会出现在站内搜索和 llms.txt 中,还会得到一张 Open Graph 图片 —— 与手写文档别无二致。

每份参考都是从 blume/reference 导入并登记在 reference 下的一个适配器:openapi() 对应 OpenAPI 文档,asyncapi() 对应 AsyncAPI 文档,graphql() 对应 GraphQL schema。每个适配器掌管自己的规范来源、挂载路由和显示选项,因此这个列表里每种类型想放多少都行。下面的配置以公开的 Petstore 规范为例,把 Blume 指向它。

import { defineConfig } from "blume";
import { openapi } from "blume/reference";

export default defineConfig({
  reference: [
    openapi({ spec: "https://petstore3.swagger.io/api/v3/openapi.json" }),
  ],
});

它把参考挂在 /reference(一个概览页)上,每个接口位于 /reference/<tag>/<operation>。spec 要么是一个 http(s) URL,要么是你项目中某个本地文件的路径。Blume 用 Scalar 的 OpenAPI 解析器解析它 —— Swagger 2.0 和 OpenAPI 3.0 规范会自动升级到 3.1。适配器只是对参考的一份纯描述 —— 不是解析后的规范 —— 因此 Blume 可以预先校验它,并把它内联进生成的站点;完全不写 reference(或留空)则不会渲染出任何参考。要记录的是事件驱动 API 或 GraphQL API 吗?请看 AsyncAPI 和 GraphQL。

参考不会自己添加页眉标签页。要让它露出来,把一个导航标签页指向它的路由 —— 这同时也限定了原生渲染器下接口侧边栏的范围:

navigation: {
  tabs: [{ label: "API", path: "/reference" }],
}

本地规范#

相对路径从你的项目根目录解析,并在构建时读取。JSON 和 YAML 都可以:

reference: [openapi({ spec: "./openapi.yaml" })],

路由#

route 控制参考挂载的位置 —— 概览页的位置,以及每条接口路由的前缀(也是你让导航标签页指向的那个路由):

reference: [
  openapi({
    route: "/api",   // 概览页在 /api,接口在 /api/<tag>/<operation>
    spec: "./openapi.yaml",
  }),
],

代码示例与 schema#

codeSamples 决定每个接口渲染哪些语言,顺序就按你列出的顺序。它默认为 ["curl", "js", "python"]。expandSchemas 让嵌套的 schema 行默认展开而非折叠:

reference: [
  openapi({
    spec: "./openapi.yaml",
    codeSamples: ["curl", "python", "go", "java", "csharp"],
    expandSchemas: true,
  }),
],

每一段示例都是针对该请求的纯文本、可直接复制粘贴的代码,使用该语言标准或最常见的 HTTP 客户端:

Id 语言 客户端 也接受
curl cURL curl bash, sh, shell
python Python requests py
js JavaScript fetch javascript
node Node.js axios nodejs, node.js
typescript TypeScript fetch ts
php PHP curl 扩展
go Go net/http golang
java Java java.net.http
ruby Ruby net/http rb
powershell PowerShell Invoke-RestMethod ps1
swift Swift URLSession
csharp C# HttpClient c#, cs
dotnet .NET RestSharp .net, dot-net
c C libcurl
cpp C++ cpr c++
kotlin Kotlin OkHttp kt
rust Rust reqwest rs
dart Dart package:http flutter

每个值都由它自己语言的引号规则包裹,因此像 If-Match: "33a64df5" 这样的请求头,或含 $ 的请求体,都会按原样送达 API。

你自己的示例#

想展示你的 SDK 而不是原始 HTTP 调用,就在规范里给某个接口加上 x-codeSamples。这是 Redocly 定义的扩展,Speakeasy 和 Stainless 都能替你写。每个条目需要一个 lang、一份 source,以及可选的 label:

paths:
  /plants:
    get:
      x-codeSamples:
        - lang: typescript
          label: SDK
          source: |
            const plants = await planter.plants.list();
        - lang: bash
          label: CLI
          source: |
            planter list

这些示例会渲染成独立的标签页,排在生成的示例之前;读者改动 Try it 表单时,它们保持原样不。旧的 x-code-samples 写法同样有效。若只想展示自己的示例,设置 codeSamples: false。

Try it 演练场#

原生渲染的接口页面默认带一个交互式 Try it 面板。Blume 直接从接口本身生成表单:每个 path、query 和 header 参数一个输入项,一个依据请求体 schema 构建的请求体编辑器,全部内容都按规范里的示例预填。取值上线路径的方式与规范的描述一致:数组和对象参数遵循各自的 style 和 explode(query 中为 tags=dog&tags=cat,path 中为 1,2,deepObject 为 filter[color]=red),而 application/x-www-form-urlencoded 或 multipart/form-data 的请求体会以表单字段而非 JSON 发送。服务器选择器列出该接口的服务器(优先它自己的 servers,其次是它所在 path 的,最后是规范的,每处 {variable} 取其 default),并留一个自由文本字段供填写其它基础 URL;认证输入项则对应接口解析出的安全要求 —— bearer token、API key 和基本凭据,OAuth2 则是一个粘贴 token 的字段(自带 access token 即可;Blume 不跑那个流程)。

面板与代码示例始终保持同步:填进表单的值会实时更新生成的示例,因此复制出来的 curl 命令总与点下 Send 时所发的请求完全一致。它也不会碍事 —— 面板以折叠状态在服务端渲染,其 JavaScript 只在读者首次打开时才加载。从不碰它的读者不会下载其中任何东西。

浏览器唯一发不出的方法是 TRACE:fetch 会拒绝它,因此 trace 接口上的 Send 只作提示而不发送,而基于 fetch 的 JavaScript 示例也只是一句说明而非代码。cURL 和 Python 的示例都能正常发出。

playground: false 就是全部的关闭开关:

reference: [openapi({ spec: "./openapi.yaml", playground: false })],

凭据#

输入到认证框里的凭据只留在内存中,刷新即消失。勾选 Remember on this device 会把它们持久化到 localStorage,作用域限定在文档站点的 origin —— 它们绝不会被发往被调用 API 之外的任何地方。无论输入了什么,代码示例里始终显示占位符(YOUR_TOKEN 之类),除非读者打开 Include my values in samples。

CORS 与代理#

与 Scalar 嵌入页一样,请求直接从浏览器发往目标 API,因此 API 必须允许来自文档站点的跨域请求(Access-Control-Allow-Origin)。做不到这一点时,设置 playground.proxy:一个 URL 会让请求经由你自己托管的代理转发,true 则启用内置的 /_api-proxy 路由(若你设置了 basePath,则位于 {basePath}/_api-proxy)—— 它需要服务端输出:一个主机适配器,例如来自 blume/deploy 的 deployment: vercel():

reference: [
  openapi({
    spec: "./openapi.yaml",
    playground: {
      proxy: true,   // 或换成你自己的 URL
    },
  }),
],

内置代理只会把请求转发到你的规范在 servers 中声明的 origin(可以出现在文档级、path 级或接口级,变量取默认值)—— 跨重定向也一样 —— 因此一个公开的文档部署无法被指向其网络内的其它主机。输入到面板里的 Custom base URL 不算已声明的服务器:启用代理后,发往它的请求会被 403 拒绝。它只转发面板自己设置的请求头 —— 填好的凭据与 header 参数,以及请求体的 Content-Type —— 因此 cookie、浏览器替文档站点附带的凭据(比如受密码保护的预览上的 HTTP Basic 认证),以及你的主机添加的请求头(X-Forwarded-For、CF-*、X-Vercel-*)都不会到达 API。它读取请求体的上限只有 4 MB(更大的会得到 413),并且它转发的每个响应都带上 Content-Security-Policy: sandbox、X-Content-Type-Options: nosniff 和 Cross-Origin-Resource-Policy: same-origin —— 对 HTML 或 SVG 还会额外带 Content-Disposition: attachment —— 这样回显输入的 API 错误页就无法在文档 origin 上执行脚本。这个代理和每个读者可调用的服务端路由一样,按读者限流。

多份规范#

用 sources 让一个适配器发布多份规范。每个来源都有自己的概览路由和接口页面,并共享适配器的显示选项。给每个来源一个 label(用于侧边栏并据此推导路由),或者显式设置 route:

reference: [
  openapi({
    sources: [
      { label: "Public API", spec: "./public.json" },   // → /reference/public-api
      { label: "Admin API", route: "/admin", spec: "./admin.json" },
    ],
  }),
],

spec 是单条目 sources 的简写,因此只有在不止一份规范时才需要动 sources。当两份规范需要不同的显示选项 —— 比如不同的代码示例集合 —— 改为列出两个 openapi() 适配器,各自带自己的 route。原生页面旁边若要嵌入 Scalar 参考,则在列表里另加一个 scalar() 适配器。来源按列表顺序解析,两个来源解析到同一路由时,先出现的那个胜出(构建会对被丢弃的那个发出警告)。

按来源的索引#

生成的页面默认参与搜索、llms.txt 和爬虫索引。次要或内容重叠的规范可以选择退出其中任意一项,而不必隐藏它的页面或把它从导航里移除:

reference: [
  openapi({
    sources: [
      { label: "Public API", route: "/api", spec: "./public.json" },
      {
        label: "Platform API",
        route: "/platform",
        spec: "./platform.json",
        includeInSearch: false,
        includeInLlms: false,
        noindex: true,
      },
    ],
  }),
],
  • includeInSearch: false 让该来源的概览页和接口不出现在站内搜索中。
  • includeInLlms: false 让它们不出现在两个 llms.txt 文件中。
  • noindex: true 添加爬虫 noindex 元数据,并把这些页面从 sitemap 中移除。

每个接口页面的 meta description 是该接口自己的 description(或 summary),后面跟一句自动生成、指明端点的话 —— “Petstore API 中 GET /pets 端点的参考文档。” —— 因此即便规范里的摘要都是简短的单行文字,每个页面仍会带上一段各不相同、正好适合搜索摘要长度的描述。这句话是英文的。如果你的规范正文用的是另一种语言,在该来源上设置 seoDescriptionSuffix: false 去掉它,只用手写的正文描述每个页面;既没有 description 也没有 summary 的接口会回退到它的标题(GET /pets),所以不会有页面带着空描述上线:

reference: [
  openapi({
    sources: [{ spec: "./openapi.de.json", seoDescriptionSuffix: false }],
  }),
],

scalar() 嵌入页只接受其中的 noindex —— 它本就在 Blume 的搜索和 llms.txt 之外,因此那两个 include* 设置在那里无事可做。

Overlays#

当规范由别的工具生成、或归另一个团队所有时,用 OpenAPI Overlay 把你的文档改动留在它之外:一个单独的文件,列出要对规范做的改动,比如更清晰的描述、公开的服务器 URL,或删除内部接口。把 overlays 写在 spec 旁边。每一个都是本地路径或 http(s) URL,并按顺序生效:

reference: [
  openapi({
    spec: "./openapi.json",
    overlays: ["./overlays/public.yaml"],
  }),
],
overlay: 1.1.0
info:
  title: Public docs
  version: 1.0.0
actions:
  - target: $.paths.*[?@['x-internal'] == true]
    remove: true
  - target: $.info
    update:
      description: The public Acme API.

每个 action 的 target 都是一个 JSONPath 表达式。update 会合并进它选中的每一个节点:对象按键逐一合并,数组追加新元素,其它值直接替换。copy 会从规范中别处合并进一个节点(Overlay 1.1),而 remove: true 则删除被选中的目标。Overlay Specification 1.0 和 1.1 都受支持。

Overlays 作用在原样的规范上,也就是 Blume 把它升级到 OpenAPI 3.1 之前,因此 overlay 针对的是你撰写规范时所用的那个版本。一切由规范构建出来的东西都会看到结果:接口页面、侧边栏、搜索、llms.txt 和 MCP 服务器。使用 sources 时,给每个来源配自己的 overlays。一个无法生效的 overlay 会像无法读取的规范一样让这份参考构建失败,因此坏掉的 overlay 无法把它本该隐藏的内容发布出去。

认证#

声明了安全要求的接口会在参数之上渲染一个 Authorization 小节,生成的代码示例也会带上占位凭据(Authorization: Bearer YOUR_TOKEN、一个 API key 请求头,或一个 query key —— 取决于所用方案)。这里没有可配置项:Blume 从规范中读取 security,因此这份参考始终与 API 实际强制的要求一致。

OpenAPI 的语义原样沿用:

  • 接口自身的 security 覆盖文档根部的默认值;security: [] 表示它公开,不渲染 Authorization 小节。
  • 多条要求条目是并列可选的 —— 渲染成“或”字样的分组;同一条目内的每个方案都必须同时满足。第一个可选项用于代码示例。
  • 空的 {} 条目表示该接口的认证是可选的,小节里也会这样写明。
  • OAuth2 的 scope 按方案逐项列出;来自 components.securitySchemes 的方案 description 会就地渲染。

Webhooks 与 callbacks#

你 API 发出的请求也会被记录下来。规范顶层 webhooks 中的每个 webhook 都像接口一样拥有自己的页面:归入它的第一个 tag,若没有 tag 则归入一个 Webhooks 分组。页面展示 payload 的 schema,以及你的端点应当回传的响应。取代 Try it 面板和代码示例的,是侧栏中的一个示例 payload —— 因为这个请求是你的 API 发出的,而不是它提供的服务。webhook 页面只列出该 webhook 自己声明的 security:规范根部的 security 保护的是对你 API 的调用,因此不适用于 API 发出的请求。

webhooks:
  newPet:
    post:
      summary: New pet added
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/Pet"
      responses:
        "200":
          description: Return a 200 to acknowledge the event.

接口的 callbacks —— 你的 API 向调用方提供的 URL 发回的请求 —— 会渲染在该操作页面的 Callbacks 小节里。每一条都展示 URL 表达式({$request.body#/callbackUrl})、它的方法、它的请求体以及预期的响应。在 components.callbacks 下声明并用 $ref 引用的 callbacks 行为一致。

Webhook 页面是真正的页面:它们在搜索、llms.txt 和 MCP 服务器中都在列,而且它们的 Markdown 会告诉智能体这是一个由 API 发出的请求,智能体因此不会去调用它。

改为嵌入 Scalar#

openapi() 始终渲染 Blume 自己的页面。若想改为在单一路由上嵌入 Scalar 那套自带的 API 参考界面 —— 它自己的侧边栏、搜索、主题和请求客户端 —— 就用一个来自 blume/reference 的 scalar() 适配器替代(或并列于)当前这个。Scalar页面介绍了这个嵌入页能做什么、不能做什么,以及如何把 Scalar 自己的选项透传进去。