在Ts.ED项目中使用Scalar构建API文档指南
前言:为什么选择Scalar作为API文档工具?
在现代Web开发中,API文档的质量直接影响开发效率和协作体验。传统的Swagger UI虽然功能强大,但在用户体验和现代化界面方面存在不足。Scalar作为新一代API文档工具,提供了更优秀的交互体验和更现代化的界面设计。
Ts.ED框架与Scalar的完美结合,让开发者能够通过简单的装饰器语法自动生成高质量的API文档,大大提升了开发效率和文档质量。
环境准备与安装
前置条件
在开始之前,请确保你的项目满足以下要求:
- Node.js 16.0+ 版本
- TypeScript 4.5+ 版本
- 已初始化的Ts.ED项目
安装Scalar依赖
# 使用npm安装
npm install --save @tsed/scalar
# 或使用yarn安装
yarn add @tsed/scalar
基础配置
在Server配置文件中添加Scalar支持:
import "@tsed/platform-express";
import "@tsed/scalar"; // 导入Scalar模块
import { Configuration } from "@tsed/di";
@Configuration({
scalar: [
{
path: "/doc", // 文档访问路径
specVersion: "3.0.1", // OpenAPI规范版本
showExplorer: true, // 显示搜索框
sortPaths: true // 路径排序
}
]
})
export class Server {}
核心功能详解
1. 模型定义与文档生成
使用Ts.ED的装饰器系统定义数据模型:
import { Description, Example, Property, Title } from "@tsed/schema";
export class CalendarModel {
@Title("日历ID")
@Description("日历的唯一标识符")
@Example("cal_123456789")
@Property()
public id: string;
@Title("日历名称")
@Description("日历的显示名称")
@Example("我的工作日历")
@Property()
public name: string;
@Title("创建时间")
@Description("日历的创建时间戳")
@Property()
public createdAt: Date;
@Title("是否公开")
@Description("标识日历是否对外公开")
@Property()
public isPublic: boolean;
}
2. 控制器端点文档
完整的控制器文档示例:
import { Controller } from "@tsed/di";
import { BodyParams, QueryParams, PathParams } from "@tsed/platform-params";
import {
Deprecated,
Description,
Get,
Post,
Put,
Delete,
Returns,
Security,
Summary
} from "@tsed/schema";
import { CalendarModel } from "./models/Calendar";
@Controller("/calendars")
@Description("日历管理API")
export class CalendarCtrl {
@Get("/:id")
@Summary("获取单个日历")
@Description("根据ID获取特定的日历信息")
@Returns(200, CalendarModel).Description("成功返回日历数据")
@Returns(404).Description("日历不存在")
@Returns(500).Description("服务器内部错误")
async getCalendar(
@PathParams("id")
@Description("日历ID")
id: string
): Promise<CalendarModel> {
return {} as CalendarModel;
}
@Get("/")
@Summary("获取日历列表")
@Description("获取所有日历的列表,支持分页和筛选")
@Returns(200, Array).Of(CalendarModel).Description("日历列表")
async getCalendars(
@QueryParams("page")
@Description("页码")
page: number = 1,
@QueryParams("limit")
@Description("每页数量")
limit: number = 10
): Promise<CalendarModel[]> {
return [];
}
@Post("/")
@Summary("创建新日历")
@Description("创建一个新的日历")
@Security("calendar_auth", "write:calendar")
@Returns(201, CalendarModel).Description("成功创建日历")
@Returns(400).Description("请求参数错误")
async createCalendar(
@BodyParams()
@Description("日历创建数据")
calendarData: Partial<CalendarModel>
): Promise<CalendarModel> {
return {} as CalendarModel;
}
@Put("/:id")
@Summary("更新日历")
@Description("更新指定日历的信息")
@Security("calendar_auth", "write:calendar")
@Returns(200, CalendarModel).Description("成功更新日历")
@Returns(404).Description("日历不存在")
async updateCalendar(
@PathParams("id") id: string,
@BodyParams() updateData: Partial<CalendarModel>
): Promise<CalendarModel> {
return {} as CalendarModel;
}
@Delete("/:id")
@Summary("删除日历")
@Description("删除指定的日历")
@Security("calendar_auth", "write:calendar")
@Returns(204).Description("成功删除日历")
@Returns(404).Description("日历不存在")
async deleteCalendar(
@PathParams("id") id: string
): Promise<void> {
// 删除逻辑
}
}
3. 高级配置选项
Scalar提供了丰富的配置选项来定制文档行为:
@Configuration({
scalar: [
{
path: "/api-doc",
specVersion: "3.1.0",
fileName: "openapi.json",
showExplorer: true,
sortPaths: true,
hidden: false,
spec: {
openapi: "3.1.0",
info: {
title: "日历管理系统API",
version: "1.0.0",
description: "提供完整的日历管理功能",
contact: {
name: "技术支持",
email: "support@example.com"
}
},
servers: [
{
url: "https://api.example.com/v1",
description: "生产环境"
},
{
url: "http://localhost:8000/v1",
description: "开发环境"
}
]
},
options: {
theme: "purple",
layout: "modern",
search: true,
defaultHttpMethod: "GET"
}
}
]
})
export class Server {}
安全配置与CSP处理
Helmet安全中间件配置
当使用Helmet时,需要正确配置CSP(Content Security Policy):
import helmet from "helmet";
@Configuration({
middlewares: [
helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: [`'self'`],
styleSrc: [`'self'`, `'unsafe-inline'`],
imgSrc: [`'self'`, "data:", "validator.scalar.io"],
scriptSrc: [`'self'`, `https: 'unsafe-inline'`]
}
}
})
],
scalar: [
{
path: "/doc",
specVersion: "3.0.1"
}
]
})
export class Server {}
认证与授权配置
@Configuration({
scalar: [
{
path: "/doc",
specVersion: "3.0.1",
options: {
authentication: {
preferredSecurityScheme: "bearer",
securitySchemes: {
bearer: {
type: "http",
scheme: "bearer",
bearerFormat: "JWT"
}
}
}
}
}
]
})
export class Server {}
多文档管理
基于装饰器的多文档
@Configuration({
scalar: [
{
path: "/admin-doc",
doc: "admin",
spec: {
info: {
title: "管理后台API",
version: "1.0.0"
}
}
},
{
path: "/user-doc",
doc: "user",
spec: {
info: {
title: "用户端API",
version: "1.0.0"
}
}
}
]
})
export class Server {}
控制器级别的文档分配
import { Docs } from "@tsed/schema";
@Controller("/admin/calendars")
@Docs("admin")
export class AdminCalendarCtrl {
// 管理端API
}
@Controller("/user/calendars")
@Docs("user")
export class UserCalendarCtrl {
// 用户端API
}
最佳实践与技巧
1. 统一的响应格式
import { Property } from "@tsed/schema";
export class ApiResponse<T> {
@Property()
success: boolean;
@Property()
message: string;
@Property()
data?: T;
@Property()
timestamp: Date = new Date();
constructor(success: boolean, message: string, data?: T) {
this.success = success;
this.message = message;
this.data = data;
}
}
// 在控制器中使用
@Returns(200, ApiResponse).Of(CalendarModel)
async getCalendar(): Promise<ApiResponse<CalendarModel>> {
return new ApiResponse(true, "操作成功", calendarData);
}
2. 错误处理与文档
export class ErrorResponse {
@Property()
statusCode: number;
@Property()
message: string;
@Property()
error: string;
@Property()
timestamp: Date;
}
// 在控制器中定义错误响应
@Returns(400, ErrorResponse).Description("参数验证失败")
@Returns(401, ErrorResponse).Description("未授权访问")
@Returns(403, ErrorResponse).Description("权限不足")
@Returns(404, ErrorResponse).Description("资源不存在")
@Returns(500, ErrorResponse).Description("服务器内部错误")
3. 枚举类型文档
import { Enum } from "@tsed/schema";
export enum CalendarStatus {
ACTIVE = "active",
INACTIVE = "inactive",
ARCHIVED = "archived"
}
export class CalendarModel {
@Property()
@Enum(CalendarStatus)
@Description("日历状态: active-活跃, inactive-未激活, archived-已归档")
status: CalendarStatus;
}
常见问题解决方案
问题1:CSP策略冲突
症状:Scalar界面无法正常加载样式和脚本 解决方案:正确配置Helmet的CSP策略
helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: [`'self'`],
styleSrc: [`'self'`, `'unsafe-inline'`],
imgSrc: [`'self'`, "data:", "validator.scalar.io"],
scriptSrc: [`'self'`, `https: 'unsafe-inline'`]
}
}
})
问题2:文档不更新
症状:代码修改后文档没有实时更新 解决方案:确保开发模式下启用热重载
# 使用Ts.ED的开发模式
npm run start:dev
问题3:类型定义不显示
症状:复杂类型在文档中显示为object 解决方案:使用@Returns明确指定返回类型
@Returns(200, ApiResponse).Of(CalendarModel).Description("成功响应")
性能优化建议
1. 生产环境配置
const isProduction = process.env.NODE_ENV === "production";
@Configuration({
scalar: isProduction ? [
{
path: "/doc",
specVersion: "3.0.1",
showExplorer: false, // 生产环境关闭搜索框
options: {
theme: "default",
layout: "classic"
}
}
] : [
{
path: "/doc",
specVersion: "3.0.1",
showExplorer: true,
options: {
theme: "purple",
layout: "modern"
}
}
]
})
export class Server {}
2. CDN优化
@Configuration({
scalar: [
{
path: "/doc",
specVersion: "3.0.1",
cdn: "https://cdn.jsdelivr.net/npm/@scalar/api-reference@latest"
}
]
})
export class Server {}
总结
通过本指南,你应该已经掌握了在Ts.ED项目中集成和使用Scalar API文档工具的全部技能。Scalar不仅提供了美观的界面,更重要的是它与Ts.ED框架的无缝集成,让API文档的维护变得简单高效。
记住良好的API文档是项目成功的关键因素之一,它不仅能提升开发效率,还能改善团队协作体验。现在就开始为你的Ts.ED项目配置Scalar,享受现代化API文档带来的便利吧!
下一步建议:
- 为现有API添加完整的文档注释
- 配置多环境文档策略
- 集成自动化文档生成到CI/CD流程
- 定期审查和更新API文档
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



