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 依赖运行时架构来生成 OpenAPI 文档。
但是,您也可以使用 OpenAPI 插件的生成器从类型生成 OpenAPI 文档,如下所示:
指定 Elysia 根文件(如果未指定,Elysia 将使用
src/index.ts
),并导出实例导入生成器并提供从项目根目录的文件路径给类型生成器
import { Elysia, t } from 'elysia'
import { openapi, fromTypes } from '@elysiajs/openapi'
export const app = new Elysia()
.use(
openapi({
references: fromTypes()
})
)
.get('/', { test: 'hello' as const })
.post('/json', ({ body, status }) => body, {
body: t.Object({
hello: t.String()
})
})
.listen(3000)
Elysia 将尝试通过读取导出的实例类型来生成 OpenAPI 文档。
这将与运行时架构共存,并且运行时架构将优先于类型定义。
生产环境
在生产环境中,您可能会将 Elysia 编译为 使用 Bun 的单一可执行文件 或 打包成单一 JavaScript 文件。
建议您预先生成声明文件(.d.ts)以向生成器提供类型声明。
import { Elysia, t } from 'elysia'
import { openapi, fromTypes } from '@elysiajs/openapi'
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, fromTypes } from '@elysiajs/openapi'
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, fromTypes } from '@elysiajs/openapi'
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)
使用 OpenAPI 的标准架构
Elysia 将尝试使用每个架构的原生方法将其转换为 OpenAPI 架构。
但是,如果架构没有提供原生方法,您可以通过提供 mapJsonSchema
来自定义将架构转换为 OpenAPI 的方式,如下所示:
Zod OpenAPI
由于 Zod 在架构上没有 toJSONSchema
方法,我们需要提供自定义映射器来将 Zod 架构转换为 OpenAPI 架构。
import openapi from '@elysiajs/openapi'
import * as z from 'zod'
openapi({
mapJsonSchema: {
zod: z.toJSONSchema
}
})
import openapi from '@elysiajs/openapi'
import { zodToJsonSchema } from 'zod-to-json-schema'
openapi({
mapJsonSchema: {
zod: zodToJsonSchema
}
})
描述路由
我们可以通过提供架构类型来添加路由信息。
但是,有时仅定义类型并不能清楚说明路由的作用。您可以使用 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: 'User password (at least 8 characters)'
})
},
{
description: 'Expected a username and password'
}
),
detail: {
summary: 'Sign in the user',
tags: ['authentication']
}
})
detail 字段默认遵循 OpenAPI V3 定义,并提供自动补全和类型安全。
然后,detail 被传递给 OpenAPI 以将描述放入 OpenAPI 路由中。
响应头
我们可以通过使用 withHeader
包装架构来添加响应头:
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: 'Expected a username and password'
}
),
detail: {
hide: true
}
}
)
标签
Elysia 可以使用 Swagger 的标签系统将端点分离成组
首先在 swagger 配置对象中定义可用的标签
new Elysia().use(
openapi({
documentation: {
tags: [
{ name: 'App', description: 'General endpoints' },
{ name: 'Auth', description: 'Authentication endpoints' }
]
}
})
)
然后在端点配置部分的 details 属性中使用该标签将端点分配到组
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 将自动处理架构生成。
将模型分离到专用部分并通过引用链接。
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: 'Require user to be logged in'
}
})
.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 Documentation',
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 令牌才能访问。