OpenAPI
Elysia 默认支持并遵循 OpenAPI 规范。
Elysia 可以通过使用 OpenAPI 插件自动生成 API 文档页面。
要生成 Swagger 页面,需安装该插件:
bun add @elysiajs/openapi
并将插件注册到服务器:
import { Elysia } from 'elysia'
import { openapi } from '@elysiajs/openapi'
new Elysia()
.use(openapi())
默认情况下,Elysia 使用 OpenAPI V3 规范和 Scalar UI
有关 OpenAPI 插件配置,请参见OpenAPI 插件页面。
从类型生成 OpenAPI
默认情况下,Elysia 依赖运行时的 schema 来生成 OpenAPI 文档。
但是,你也可以通过使用 OpenAPI 插件中的生成器根据类型生成 OpenAPI 文档,方法如下:
指定项目的根文件(通常是
src/index.ts
),并导出一个实例导入生成器并向类型生成器提供相对于项目根的文件路径
import { Elysia, t } from 'elysia'
import { openapi } from '@elysiajs/openapi'
import { fromTypes } from '@elysiajs/openapi/gen'
export const app = new Elysia()
.use(
openapi({
references: fromTypes('src/index.ts')
})
)
.get('/', { test: 'hello' as const })
.post('/json', ({ body, status }) => body, {
body: t.Object({
hello: t.String()
})
})
.listen(3000)
Elysia 会尝试通过读取导出实例的类型来生成 OpenAPI 文档。
这将与运行时的 schema 共存,且运行时 schema 会优先于类型定义。
生产环境
在生产环境中,你很可能会将 Elysia 编译为 Bun 单个可执行文件 或 打包为单个 JavaScript 文件。
推荐预先生成声明文件(.d.ts)来提供类型声明给生成器。
import { Elysia, t } from 'elysia'
import { openapi } from '@elysiajs/openapi'
import { fromTypes } from '@elysiajs/openapi/gen'
const app = new Elysia()
.use(
openapi({
references: fromTypes(
process.env.NODE_ENV === 'production'
? 'dist/index.d.ts'
: 'src/index.ts'
)
})
)
类型生成遇到问题?
注意事项:根路径
由于推断项目根目录不可靠,建议明确提供项目根路径,这样生成器才能正确运行,尤其在使用 monorepo 时。
import { Elysia, t } from 'elysia'
import { openapi } from '@elysiajs/openapi'
import { fromTypes } from '@elysiajs/openapi/gen'
export const app = new Elysia()
.use(
openapi({
references: fromTypes('src/index.ts', {
projectRoot: path.join('..', import.meta.dir)
})
})
)
.get('/', { test: 'hello' as const })
.post('/json', ({ body, status }) => body, {
body: t.Object({
hello: t.String()
})
})
.listen(3000)
自定义 tsconfig.json
如果你有多个 tsconfig.json
,务必指定正确的 tsconfig.json
文件用于类型生成。
import { Elysia, t } from 'elysia'
import { openapi } from '@elysiajs/openapi'
import { fromTypes } from '@elysiajs/openapi/gen'
export const app = new Elysia()
.use(
openapi({
references: fromTypes('src/index.ts', {
// 这是相对于项目根目录的路径
tsconfigPath: 'tsconfig.dts.json'
})
})
)
.get('/', { test: 'hello' as const })
.post('/json', ({ body, status }) => body, {
body: t.Object({
hello: t.String()
})
})
.listen(3000)
描述路由
我们可以通过提供 schema 类型来添加路由信息。
但是,有时仅定义类型并不能清晰表明路由的作用。你可以使用detail 字段显式描述路由。
import { Elysia, t } from 'elysia'
import { openapi } from '@elysiajs/openapi'
new Elysia()
.use(openapi())
.post(
'/sign-in',
({ body }) => body, {
body: t.Object(
{
username: t.String(),
password: t.String({
minLength: 8,
description: '用户密码(至少8个字符)'
})
},
{
description: '需要用户名和密码'
}
),
detail: {
summary: '用户登录',
tags: ['authentication']
}
})
detail 字段遵循 OpenAPI V3 定义,默认具备自动补全和类型安全。
detail 会传递给 OpenAPI,以为路由添加描述。
响应头
我们可以通过使用 withHeader
包装 schema 来添加响应头:
import { Elysia, t } from 'elysia'
import { openapi, withHeader } from '@elysiajs/openapi'
new Elysia()
.use(openapi())
.get(
'/thing',
({ body, set }) => {
set.headers['x-powered-by'] = 'Elysia'
return body
},
{
response: withHeader(
t.Literal('Hi'),
{
'x-powered-by': t.Literal('Elysia')
}
)
}
)
注意,withHeader
仅作注解用,不会强制或验证实际响应头。你需要手动设置响应头。
隐藏路由
你可以通过将 detail.hide
设置为 true
来隐藏 Swagger 页面中的路由。
import { Elysia, t } from 'elysia'
import { openapi } from '@elysiajs/openapi'
new Elysia()
.use(openapi())
.post(
'/sign-in',
({ body }) => body,
{
body: t.Object(
{
username: t.String(),
password: t.String()
},
{
description: '需要用户名和密码'
}
),
detail: {
hide: true
}
}
)
标签
Elysia 可以通过 Swagger 的标签系统将端点分组。
首先,在 Swagger 配置对象中定义可用的标签
new Elysia().use(
openapi({
documentation: {
tags: [
{ name: 'App', description: '通用端点' },
{ name: 'Auth', description: '身份验证端点' }
]
}
})
)
然后,在端点配置部分的 detail 属性中为该端点指定标签
new Elysia()
.get('/', () => 'Hello Elysia', {
detail: {
tags: ['App']
}
})
.group('/auth', (app) =>
app.post(
'/sign-up',
({ body }) =>
db.user.create({
data: body,
select: {
id: true,
username: true
}
}),
{
detail: {
tags: ['Auth']
}
}
)
)
这将生成如下的 Swagger 页面
标签分组
Elysia 也可接受标签,将整个实例或路由组归到特定标签下。
import { Elysia, t } from 'elysia'
new Elysia({
tags: ['user']
})
.get('/user', 'user')
.get('/admin', 'admin')
模型
通过使用引用模型,Elysia 会自动处理 schema 生成。
将模型分离到专门的部分并通过引用链接。
new Elysia()
.model({
User: t.Object({
id: t.Number(),
username: t.String()
})
})
.get('/user', () => ({ id: 1, username: 'saltyaom' }), {
response: {
200: 'User'
},
detail: {
tags: ['User']
}
})
守卫
另外,Elysia 也可通过守卫将整个实例或路由组关联到特定守卫。
import { Elysia, t } from 'elysia'
new Elysia()
.guard({
detail: {
description: '要求用户必须登录'
}
})
.get('/user', 'user')
.get('/admin', 'admin')
更改 OpenAPI 端点
你可以通过在插件配置中设置 path 来更改 OpenAPI 端点。
import { Elysia } from 'elysia'
import { openapi } from '@elysiajs/openapi'
new Elysia()
.use(
openapi({
path: '/v2/openapi'
})
)
.listen(3000)
自定义 OpenAPI 信息
我们可以通过在插件配置中设置 documentation.info 来自定义 OpenAPI 信息。
import { Elysia } from 'elysia'
import { openapi } from '@elysiajs/openapi'
new Elysia()
.use(
openapi({
documentation: {
info: {
title: 'Elysia 文档',
version: '1.0.0'
}
}
})
)
.listen(3000)
这对于以下情况非常有用:
- 添加标题
- 设置 API 版本
- 添加描述说明我们的 API 是关于什么的
- 解释可用的标签,以及每个标签的含义
安全配置
为了保护你的 API 端点,你可以在 Swagger 配置中定义安全方案。下面的示例演示如何使用 Bearer 认证 (JWT) 保护端点:
new Elysia().use(
openapi({
documentation: {
components: {
securitySchemes: {
bearerAuth: {
type: 'http',
scheme: 'bearer',
bearerFormat: 'JWT'
}
}
}
}
})
)
export const addressController = new Elysia({
prefix: '/address',
detail: {
tags: ['Address'],
security: [
{
bearerAuth: []
}
]
}
})
这将确保 /address
前缀下的所有端点都需要有效的 JWT 令牌才能访问。