Skip to content

Swagger 中间件

该中间件为 Vafast 提供了完整的 Swagger/OpenAPI 文档生成和 UI 展示功能,支持 Scalar 和 Swagger UI 两种界面,让 API 文档更加专业和易用。

安装

安装命令:

bash
bun add @vafast/swagger

基本用法

typescript
import { Server, createHandler } from 'vafast'
import { swagger } from '@vafast/swagger'

// 创建 Swagger 中间件
const swaggerMiddleware = swagger({
    provider: 'scalar',
    documentation: {
        info: {
            title: 'My API',
            version: '1.0.0'
        }
    }
})

// 定义路由
const routes = [
    {
        method: 'GET',
        path: '/api/',
        handler: createHandler(() => {
            return { message: 'Hello API' }
        })
    }
]

// 创建服务器
const server = new Server(routes)

// 导出 fetch 函数,应用 Swagger 中间件
export default {
    fetch: (req: Request) => {
        return swaggerMiddleware(req, () => server.fetch(req))
    }
}

配置选项

VafastSwaggerConfig

typescript
interface VafastSwaggerConfig<Path extends string = '/swagger'> {
  /** 文档提供者,默认:'scalar' */
  provider?: 'scalar' | 'swagger-ui'
  
  /** Scalar 版本,默认:'latest' */
  scalarVersion?: string
  
  /** Scalar CDN 地址,默认:官方 CDN */
  scalarCDN?: string
  
  /** Scalar 配置选项 */
  scalarConfig?: Record<string, any>
  
  /** OpenAPI 文档配置 */
  documentation?: OpenAPIDocumentation
  
  /** Swagger UI 版本,默认:'4.18.2' */
  version?: string
  
  /** 是否排除静态文件,默认:true */
  excludeStaticFile?: boolean
  
  /** 文档路径,默认:'/swagger' */
  path?: Path
  
  /** OpenAPI 规范路径,默认:'${path}/json' */
  specPath?: string
  
  /** 排除的路径或方法 */
  exclude?: string | RegExp | (string | RegExp)[]
  
  /** Swagger UI 选项 */
  swaggerOptions?: Record<string, any>
  
  /** 主题配置 */
  theme?: string | { light: string; dark: string }
  
  /** 是否启用自动暗色模式,默认:true */
  autoDarkMode?: boolean
  
  /** 排除的 HTTP 方法,默认:['OPTIONS'] */
  excludeMethods?: string[]
  
  /** 排除的标签 */
  excludeTags?: string[]
}

OpenAPIDocumentation

typescript
interface OpenAPIDocumentation {
  /** API 基本信息 */
  info?: OpenAPIInfo
  
  /** API 标签 */
  tags?: OpenAPITag[]
  
  /** 组件定义 */
  components?: OpenAPIComponents
  
  /** API 路径定义 */
  paths?: Record<string, any>
}

interface OpenAPIInfo {
  title?: string
  description?: string
  version?: string
}

interface OpenAPITag {
  name: string
  description?: string
}

interface OpenAPISchema {
  type: string
  properties?: Record<string, any>
  required?: string[]
}

interface OpenAPISecurityScheme {
  type: string
  scheme?: string
  bearerFormat?: string
  description?: string
}

interface OpenAPIComponents {
  schemas?: Record<string, OpenAPISchema>
  securitySchemes?: Record<string, OpenAPISecurityScheme>
}

使用模式

1. 基本 Scalar 文档

typescript
import { Server, createHandler } from 'vafast'
import { swagger } from '@vafast/swagger'

const swaggerMiddleware = swagger({
    provider: 'scalar',
    documentation: {
        info: {
            title: 'Vafast API',
            description: 'A modern TypeScript web framework API',
            version: '1.0.0'
        },
        tags: [
            {
                name: 'Users',
                description: 'User management endpoints'
            },
            {
                name: 'Auth',
                description: 'Authentication endpoints'
            }
        ]
    }
})

const routes = [
    {
        method: 'GET',
        path: '/api/users',
        handler: createHandler(() => {
            return { users: [] }
        })
    }
]

const server = new Server(routes)

export default {
    fetch: (req: Request) => {
        return swaggerMiddleware(req, () => server.fetch(req))
    }
}

2. Swagger UI 文档

typescript
import { Server, createHandler } from 'vafast'
import { swagger } from '@vafast/swagger'

const swaggerMiddleware = swagger({
    provider: 'swagger-ui',
    version: '4.18.2',
    documentation: {
        info: {
            title: 'Vafast API',
            description: 'API documentation with Swagger UI',
            version: '1.0.0'
        }
    },
    swaggerOptions: {
        persistAuthorization: true,
        displayOperationId: true,
        filter: true
    },
    theme: 'https://unpkg.com/swagger-ui-dist@4.18.2/swagger-ui.css',
    autoDarkMode: true
})

const routes = [
    {
        method: 'GET',
        path: '/api/health',
        handler: createHandler(() => {
            return { status: 'OK' }
        })
    }
]

const server = new Server(routes)

export default {
    fetch: (req: Request) => {
        return swaggerMiddleware(req, () => server.fetch(req))
    }
}

3. 自定义路径和配置

typescript
import { Server, createHandler } from 'vafast'
import { swagger } from '@vafast/swagger'

const swaggerMiddleware = swagger({
    provider: 'scalar',
    path: '/docs',                    // 自定义文档路径
    specPath: '/docs/openapi.json',   // 自定义规范路径
    scalarVersion: '1.0.0',           // 指定 Scalar 版本
    scalarCDN: 'https://cdn.example.com/scalar', // 自定义 CDN
    scalarConfig: {
        theme: 'light',
        search: true,
        navigation: true
    },
    documentation: {
        info: {
            title: 'Custom API',
            version: '2.0.0'
        }
    }
})

const routes = [
    {
        method: 'GET',
        path: '/api/data',
        handler: createHandler(() => {
            return { data: 'example' }
        })
    }
]

const server = new Server(routes)

export default {
    fetch: (req: Request) => {
        return swaggerMiddleware(req, () => server.fetch(req))
    }
}

4. 完整的 API 文档

typescript
import { Server, createHandler } from 'vafast'
import { swagger } from '@vafast/swagger'

const swaggerMiddleware = swagger({
    provider: 'scalar',
    documentation: {
        info: {
            title: 'Complete API',
            description: 'A comprehensive API with full documentation',
            version: '1.0.0'
        },
        tags: [
            {
                name: 'Users',
                description: 'User management operations'
            },
            {
                name: 'Posts',
                description: 'Blog post operations'
            },
            {
                name: 'Auth',
                description: 'Authentication and authorization'
            }
        ],
        components: {
            schemas: {
                User: {
                    type: 'object',
                    properties: {
                        id: { type: 'string', format: 'uuid' },
                        username: { type: 'string', minLength: 3 },
                        email: { type: 'string', format: 'email' },
                        createdAt: { type: 'string', format: 'date-time' }
                    },
                    required: ['username', 'email']
                },
                Post: {
                    type: 'object',
                    properties: {
                        id: { type: 'string', format: 'uuid' },
                        title: { type: 'string', minLength: 1 },
                        content: { type: 'string', minLength: 10 },
                        authorId: { type: 'string', format: 'uuid' },
                        published: { type: 'boolean', default: false }
                    },
                    required: ['title', 'content', 'authorId']
                }
            },
            securitySchemes: {
                BearerAuth: {
                    type: 'http',
                    scheme: 'bearer',
                    bearerFormat: 'JWT',
                    description: 'Enter your JWT token in the format: Bearer <token>'
                },
                ApiKeyAuth: {
                    type: 'apiKey',
                    in: 'header',
                    name: 'X-API-Key',
                    description: 'Enter your API key'
                }
            }
        },
        paths: {
            '/api/users': {
                get: {
                    summary: 'Get all users',
                    tags: ['Users'],
                    responses: {
                        '200': {
                            description: 'List of users',
                            content: {
                                'application/json': {
                                    schema: {
                                        type: 'array',
                                        items: { $ref: '#/components/schemas/User' }
                                    }
                                }
                            }
                        }
                    }
                },
                post: {
                    summary: 'Create a new user',
                    tags: ['Users'],
                    requestBody: {
                        required: true,
                        content: {
                            'application/json': {
                                schema: { $ref: '#/components/schemas/User' }
                            }
                        }
                    },
                    responses: {
                        '201': {
                            description: 'User created successfully',
                            content: {
                                'application/json': {
                                    schema: { $ref: '#/components/schemas/User' }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
})

const routes = [
    {
        method: 'GET',
        path: '/api/users',
        handler: createHandler(() => {
            return { users: [] }
        })
    },
    {
        method: 'POST',
        path: '/api/users',
        handler: createHandler(async (req: Request) => {
            const userData = await req.json()
            return { ...userData, id: 'generated-id' }
        })
    }
]

const server = new Server(routes)

export default {
    fetch: (req: Request) => {
        return swaggerMiddleware(req, () => server.fetch(req))
    }
}

5. 中间件集成

typescript
import { Server, createHandler } from 'vafast'
import { swagger } from '@vafast/swagger'

// 创建 Swagger 中间件
const swaggerMiddleware = swagger({
    provider: 'scalar',
    path: '/api-docs',
    documentation: {
        info: {
            title: 'Middleware API',
            version: '1.0.0'
        }
    }
})

// 定义路由
const routes = [
    {
        method: 'GET',
        path: '/',
        handler: createHandler(() => {
            return { message: 'API is running' }
        })
    },
    {
        method: 'GET',
        path: '/api/status',
        handler: createHandler(() => {
            return { status: 'healthy', timestamp: new Date().toISOString() }
        })
    }
]

const server = new Server(routes)

// 创建中间件包装器
const createMiddlewareWrapper = (middleware: any, handler: any) => {
    return async (req: Request) => {
        return middleware(req, () => handler(req))
    }
}

// 导出带中间件的 fetch 函数
export default {
    fetch: createMiddlewareWrapper(swaggerMiddleware, server.fetch.bind(server))
}

完整示例

typescript
import { Server, createHandler } from 'vafast'
import { swagger } from '@vafast/swagger'

// 创建 Swagger 中间件
const swaggerMiddleware = swagger({
    provider: 'scalar',
    scalarVersion: 'latest',
    scalarConfig: {
        theme: 'light',
        search: true,
        navigation: true,
        sidebar: true
    },
    documentation: {
        info: {
            title: 'Vafast Swagger Example',
            description: 'A complete example of Vafast with Swagger documentation',
            version: '0.8.1'
        },
        tags: [
            {
                name: 'Test',
                description: 'Test endpoints for demonstration'
            },
            {
                name: 'Users',
                description: 'User management endpoints'
            },
            {
                name: 'Files',
                description: 'File upload and management'
            }
        ],
        components: {
            schemas: {
                User: {
                    type: 'object',
                    properties: {
                        username: { type: 'string', minLength: 3 },
                        email: { type: 'string', format: 'email' },
                        age: { type: 'number', minimum: 0 }
                    },
                    required: ['username', 'email']
                },
                ApiResponse: {
                    type: 'object',
                    properties: {
                        success: { type: 'boolean' },
                        message: { type: 'string' },
                        data: { type: 'object' }
                    }
                }
            },
            securitySchemes: {
                JwtAuth: {
                    type: 'http',
                    scheme: 'bearer',
                    bearerFormat: 'JWT',
                    description: 'Enter JWT Bearer token **_only_**'
                },
                ApiKeyAuth: {
                    type: 'apiKey',
                    in: 'header',
                    name: 'X-API-Key',
                    description: 'Enter your API key'
                }
            }
        },
        paths: {
            '/api/test': {
                get: {
                    summary: 'Test endpoint',
                    description: 'A simple test endpoint',
                    tags: ['Test'],
                    responses: {
                        '200': {
                            description: 'Successful response',
                            content: {
                                'application/json': {
                                    schema: { $ref: '#/components/schemas/ApiResponse' }
                                }
                            }
                        }
                    }
                }
            },
            '/api/users': {
                get: {
                    summary: 'Get all users',
                    tags: ['Users'],
                    security: [{ JwtAuth: [] }],
                    responses: {
                        '200': {
                            description: 'List of users',
                            content: {
                                'application/json': {
                                    schema: {
                                        type: 'array',
                                        items: { $ref: '#/components/schemas/User' }
                                    }
                                }
                            }
                        },
                        '401': {
                            description: 'Unauthorized'
                        }
                    }
                },
                post: {
                    summary: 'Create a new user',
                    tags: ['Users'],
                    requestBody: {
                        required: true,
                        content: {
                            'application/json': {
                                schema: { $ref: '#/components/schemas/User' }
                            }
                        }
                    },
                    responses: {
                        '201': {
                            description: 'User created successfully',
                            content: {
                                'application/json': {
                                    schema: { $ref: '#/components/schemas/User' }
                                }
                            }
                        },
                        '400': {
                            description: 'Bad request'
                        }
                    }
                }
            }
        }
    },
    swaggerOptions: {
        persistAuthorization: true,
        displayOperationId: true,
        filter: true,
        showExtensions: true
    }
})

// 定义 API 路由
const routes = [
    {
        method: 'GET',
        path: '/',
        handler: createHandler(() => {
            return {
                message: 'Vafast Swagger Example API',
                version: '1.0.0',
                documentation: '/swagger',
                openapi: '/swagger/json'
            }
        })
    },
    {
        method: 'GET',
        path: '/api/test',
        handler: createHandler(() => {
            return {
                success: true,
                message: 'Test endpoint working',
                data: { timestamp: new Date().toISOString() }
            }
        })
    },
    {
        method: 'GET',
        path: '/api/users',
        handler: createHandler(() => {
            return [
                { username: 'john_doe', email: 'john@example.com', age: 30 },
                { username: 'jane_smith', email: 'jane@example.com', age: 25 }
            ]
        })
    },
    {
        method: 'POST',
        path: '/api/users',
        handler: createHandler(async (req: Request) => {
            const userData = await req.json()
            return {
                ...userData,
                id: `user_${Date.now()}`,
                createdAt: new Date().toISOString()
            }
        })
    },
    {
        method: 'GET',
        path: '/api/health',
        handler: createHandler(() => {
            return {
                status: 'healthy',
                uptime: process.uptime(),
                timestamp: new Date().toISOString()
            }
        })
    }
]

// 创建服务器
const server = new Server(routes)

// 导出带 Swagger 中间件的 fetch 函数
export default {
    fetch: (req: Request) => {
        return swaggerMiddleware(req, () => server.fetch(req))
    }
}

console.log('🚀 Vafast Swagger Example Server 启动成功!')
console.log('📚 API 文档:/swagger')
console.log('🔗 OpenAPI 规范:/swagger/json')
console.log('🌐 健康检查:/api/health')

测试示例

typescript
import { describe, expect, it } from 'bun:test'
import { Server, createHandler } from 'vafast'
import { swagger } from '@vafast/swagger'

describe('Vafast Swagger Plugin', () => {
    it('should create swagger middleware', () => {
        const swaggerMiddleware = swagger({
            provider: 'scalar',
            documentation: {
                info: {
                    title: 'Test API',
                    version: '1.0.0'
                }
            }
        })

        expect(swaggerMiddleware).toBeDefined()
        expect(typeof swaggerMiddleware).toBe('function')
    })

    it('should serve Scalar documentation page', async () => {
        const swaggerMiddleware = swagger({
            provider: 'scalar',
            path: '/docs'
        })

        const app = new Server([
            {
                method: 'GET',
                path: '/',
                handler: createHandler(() => {
                    return 'Hello, API!'
                })
            }
        ])

        // 应用中间件
        const wrappedFetch = (req: Request) => {
            return swaggerMiddleware(req, () => app.fetch(req))
        }

        // 测试访问 Scalar 文档页面
        const res = await wrappedFetch(new Request('http://localhost/docs'))
        expect(res.status).toBe(200)
        expect(res.headers.get('content-type')).toContain('text/html')

        const html = await res.text()
        expect(html).toContain('scalar')
    })

    it('should serve OpenAPI specification', async () => {
        const swaggerMiddleware = swagger({
            provider: 'scalar',
            path: '/docs',
            specPath: '/docs/json'
        })

        const app = new Server([
            {
                method: 'GET',
                path: '/',
                handler: createHandler(() => {
                    return 'Hello, API!'
                })
            }
        ])

        // 应用中间件
        const wrappedFetch = (req: Request) => {
            return swaggerMiddleware(req, () => app.fetch(req))
        }

        // 测试访问 OpenAPI 规范
        const res = await wrappedFetch(
            new Request('http://localhost/docs/json')
        )
        expect(res.status).toBe(200)
        expect(res.headers.get('content-type')).toContain('application/json')

        const spec = await res.json()
        expect(spec.openapi).toBe('3.0.3')
        expect(spec.info.title).toBe('Vafast API')
    })

    it('should handle custom documentation info', async () => {
        const swaggerMiddleware = swagger({
            provider: 'scalar',
            documentation: {
                info: {
                    title: 'Custom API',
                    description: 'Custom API description',
                    version: '2.0.0'
                },
                tags: [
                    {
                        name: 'Users',
                        description: 'User management endpoints'
                    }
                ]
            }
        })

        const app = new Server([])
        const wrappedFetch = (req: Request) => {
            return swaggerMiddleware(req, () => app.fetch(req))
        }

        const res = await wrappedFetch(
            new Request('http://localhost/swagger/json')
        )
        const spec = await res.json()

        expect(spec.info.title).toBe('Custom API')
        expect(spec.info.description).toBe('Custom API description')
        expect(spec.info.version).toBe('2.0.0')
        expect(spec.tags).toHaveLength(1)
        expect(spec.tags[0].name).toBe('Users')
    })

    it('should handle Swagger UI provider', async () => {
        const swaggerMiddleware = swagger({
            provider: 'swagger-ui',
            version: '4.18.2'
        })

        const app = new Server([])
        const wrappedFetch = (req: Request) => {
            return swaggerMiddleware(req, () => app.fetch(req))
        }

        const res = await wrappedFetch(new Request('http://localhost/swagger'))
        const html = await res.text()

        expect(res.status).toBe(200)
        expect(html).toContain('swagger-ui')
        expect(html).toContain('4.18.2')
    })

    it('should pass through non-swagger requests', async () => {
        const swaggerMiddleware = swagger({
            provider: 'scalar'
        })

        const app = new Server([
            {
                method: 'GET',
                path: '/api/data',
                handler: createHandler(() => {
                    return { message: 'Data endpoint' }
                })
            }
        ])

        const wrappedFetch = (req: Request) => {
            return swaggerMiddleware(req, () => app.fetch(req))
        }

        const res = await wrappedFetch(
            new Request('http://localhost/api/data')
        )
        const data = await res.json()

        expect(res.status).toBe(200)
        expect(data.message).toBe('Data endpoint')
    })

    it('should handle custom path configuration', async () => {
        const swaggerMiddleware = swagger({
            provider: 'scalar',
            path: '/custom-docs',
            specPath: '/custom-docs/spec'
        })

        const app = new Server([])
        const wrappedFetch = (req: Request) => {
            return swaggerMiddleware(req, () => app.fetch(req))
        }

        // 测试自定义文档路径
        const docsRes = await wrappedFetch(
            new Request('http://localhost/custom-docs')
        )
        expect(docsRes.status).toBe(200)

        // 测试自定义规范路径
        const specRes = await wrappedFetch(
            new Request('http://localhost/custom-docs/spec')
        )
        expect(specRes.status).toBe(200)
        expect(specRes.headers.get('content-type')).toContain('application/json')
    })
})

特性

  • 双界面支持: 支持 Scalar 和 Swagger UI 两种文档界面
  • 自动生成: 自动生成 OpenAPI 3.0.3 规范
  • 灵活配置: 丰富的配置选项和自定义支持
  • 中间件集成: 无缝集成到 Vafast 应用
  • 类型安全: 完整的 TypeScript 类型支持
  • 主题定制: 支持自定义主题和暗色模式
  • CDN 配置: 支持自定义 CDN 地址
  • 路径配置: 灵活的文档和规范路径配置

最佳实践

1. 选择合适的提供者

typescript
// 现代、美观的界面,推荐用于生产环境
const scalarMiddleware = swagger({
    provider: 'scalar',
    scalarVersion: 'latest'
})

// 传统、功能丰富的界面,适合开发调试
const swaggerUIMiddleware = swagger({
    provider: 'swagger-ui',
    version: '4.18.2'
})

2. 结构化文档组织

typescript
const swaggerMiddleware = swagger({
    provider: 'scalar',
    documentation: {
        info: {
            title: 'API Name',
            description: 'Clear API description',
            version: '1.0.0'
        },
        tags: [
            { name: 'Users', description: 'User management' },
            { name: 'Auth', description: 'Authentication' }
        ],
        components: {
            schemas: {
                // 定义可重用的数据模型
            },
            securitySchemes: {
                // 定义安全方案
            }
        }
    }
})

3. 安全配置

typescript
const swaggerMiddleware = swagger({
    provider: 'scalar',
    documentation: {
        components: {
            securitySchemes: {
                BearerAuth: {
                    type: 'http',
                    scheme: 'bearer',
                    bearerFormat: 'JWT'
                }
            }
        }
    }
})

4. 生产环境配置

typescript
const swaggerMiddleware = swagger({
    provider: 'scalar',
    path: '/api/docs',           // 使用子路径
    excludeStaticFile: true,      // 排除静态文件
    scalarCDN: 'https://cdn.example.com/scalar', // 使用自己的 CDN
    documentation: {
        info: {
            title: 'Production API',
            version: process.env.API_VERSION || '1.0.0'
        }
    }
})

注意事项

  1. 路径冲突: 确保 Swagger 路径不与 API 路由冲突
  2. 安全考虑: 生产环境建议使用子路径,避免暴露在根路径
  3. CDN 配置: 生产环境建议使用自己的 CDN 地址
  4. 版本管理: 保持 Swagger UI 和 Scalar 版本更新
  5. 文档维护: 及时更新 API 文档以保持同步
  6. 性能影响: 中间件会拦截所有请求,注意性能影响
  7. 类型定义: 充分利用 TypeScript 类型来生成准确的文档

版本信息

  • 当前版本: 0.0.1
  • Vafast 兼容性: >= 0.1.12
  • OpenAPI 版本: 3.0.3
  • 支持界面: Scalar (最新版本) + Swagger UI (4.18.2)

相关链接