Swagger Plugin
The Swagger plugin automatically generates OpenAPI 3.0 documentation for your Balda API.
Features
- Automatic Route Discovery: Automatically discovers and documents all your routes
- Multiple UI Options: Choose between Swagger UI, ReDoc, or RapiDoc
- Type Integration: Uses your validation schemas to generate accurate request/response documentation
- Security Support: Document authentication schemes and security requirements
- Custom Models: Define reusable data models for your API
Global Configuration
Basic Setup
import { Server } from 'balda';
const server = new Server({
port: 3000,
swagger: {
type: "standard", // "standard" | "redoc" | "rapidoc"
path: "/docs",
title: "My API Documentation",
description: "API documentation for my application",
version: "1.0.0",
servers: ["http://localhost:3000"]
}
});
Advanced Configuration
const server = new Server({
port: 3000,
swagger: {
type: "standard",
path: "/api-docs",
title: "E-Commerce API",
description: "Complete API for our e-commerce platform",
version: "2.1.0",
servers: [
"http://localhost:3000",
"https://api.staging.example.com",
"https://api.example.com"
],
models: {
User: {
type: "object",
properties: {
id: { type: "string", format: "uuid" },
email: { type: "string", format: "email" },
name: { type: "string" },
createdAt: { type: "string", format: "date-time" }
},
required: ["email", "name"]
},
Product: {
type: "object",
properties: {
id: { type: "string" },
name: { type: "string" },
price: { type: "number" },
category: { type: "string" }
},
required: ["name", "price"]
}
}
}
});
Route-Level Documentation
Basic Route Documentation
With Controller Decorators
import { controller, get, post, validate } from 'balda';
import { z } from 'zod';
const CreateUserSchema = z.object({
email: z.string().email(),
name: z.string(),
password: z.string().min(8)
});
const UserResponse = z.object({
id: z.string(),
email: z.string(),
name: z.string()
});
@controller('/users')
export class UsersController {
@get('/', {
swagger: {
name: "Get All Users",
description: "Retrieve a list of all users",
service: "users",
responses: {
200: z.array(UserResponse)
}
}
})
async getAllUsers(req: Request, res: Response) {
const users = await getUsers();
res.json(users);
}
@post('/')
@validate.body(CreateUserSchema) // Body schema for validation
async createUser(
req: Request,
res: Response,
body: z.infer<typeof CreateUserSchema>
) {
const user = await createUser(body);
res.created({ user, message: "User created successfully" });
}
}
With Inline Routes
import { Server } from 'balda';
import { z } from 'zod';
const server = new Server({ port: 3000 });
const CreateUserSchema = z.object({
email: z.string().email(),
name: z.string(),
password: z.string().min(8)
});
// Inline validation - automatically documents in Swagger
server.router.post('/users', {
body: CreateUserSchema, // Automatically added to Swagger docs
responses: {
201: UserResponse,
400: ErrorSchema
},
swagger: {
name: "Create User",
description: "Create a new user account",
service: "users",
}
}, (req, res) => {
// req.body is typed and validated
const user = createUser(req.body);
res.created({ user, message: "User created successfully" });
});
// Query validation
const PaginationSchema = z.object({
page: z.coerce.number().min(1).default(1),
limit: z.coerce.number().min(1).max(100).default(10)
});
server.router.get('/users', {
query: PaginationSchema, // Automatically added to Swagger docs
responses: {
200: z.array(UserResponse)
},
swagger: {
name: "List Users",
}
}, (req, res) => {
const users = getUsersPaginated(req.query.page, req.query.limit);
res.json(users);
});
Automatic Schema Documentation
When you specify body or query at the route level, they're automatically included in the Swagger documentation. You don't need to specify them again in the swagger options.
Path Parameters
@get('/:id', {
swagger: {
name: "Get User by ID",
description: "Retrieve a specific user by their ID",
service: "users",
params: {
type: "object",
properties: {
id: { type: "string", format: "uuid" }
}
},
responses: {
"200": { $ref: "#/components/schemas/User" },
"404": {
type: "object",
properties: {
error: { type: "string" }
}
}
}
}
})
async getUserById(req: Request, res: Response) {
const { id } = req.params;
const user = await getUserById(id);
if (!user) {
return res.notFound({ error: "User not found" });
}
res.json(user);
}
Query Parameters
@get('/search', {
swagger: {
name: "Search Users",
description: "Search users with filters",
service: "users",
query: {
type: "object",
properties: {
q: { type: "string", description: "Search query" },
limit: { type: "number", minimum: 1, maximum: 100, default: 10 },
offset: { type: "number", minimum: 0, default: 0 },
sort: { type: "string", enum: ["name", "email", "createdAt"] }
},
required: ["q"]
},
responses: {
"200": {
type: "object",
properties: {
users: {
type: "array",
items: { $ref: "#/components/schemas/User" }
},
total: { type: "number" },
limit: { type: "number" },
offset: { type: "number" }
}
}
}
}
})
async searchUsers(req: Request, res: Response) {
const { q, limit = 10, offset = 0, sort } = req.query;
const result = await searchUsers({ q, limit, offset, sort });
res.json(result);
}
Security Requirements
@post('/profile', {
swagger: {
name: "Update Profile",
description: "Update user profile (requires authentication)",
service: "users",
security: [
{ type: "bearer", bearerFormat: "JWT" }
],
requestBody: {
type: "object",
properties: {
name: { type: "string" },
bio: { type: "string" }
}
},
responses: {
"200": { $ref: "#/components/schemas/User" },
"401": {
type: "object",
properties: {
error: { type: "string" }
}
}
}
}
})
async updateProfile(req: Request, res: Response) {
// Implementation with authentication
const user = await updateUserProfile(req.user.id, req.body);
res.json(user);
}
File Upload Documentation
@post('/avatar', {
swagger: {
name: "Upload Avatar",
description: "Upload user avatar image",
service: "users",
bodyType: "form-data",
requestBody: {
type: "object",
properties: {
avatar: {
type: "string",
format: "binary",
description: "Avatar image file"
}
},
required: ["avatar"]
},
responses: {
"200": {
type: "object",
properties: {
url: { type: "string", format: "uri" },
message: { type: "string" }
}
}
}
}
})
async uploadAvatar(req: Request, res: Response) {
const file = req.files.avatar;
const url = await uploadFile(file);
res.json({ url, message: "Avatar uploaded successfully" });
}
Excluding Routes
@get('/internal/health', {
swagger: {
excludeFromSwagger: true
}
})
async healthCheck(req: Request, res: Response) {
res.json({ status: "ok" });
}
UI Options
Swagger UI (Standard)
swagger: {
type: "standard",
path: "/docs"
}
ReDoc
swagger: {
type: "redoc",
path: "/docs"
}
RapiDoc
swagger: {
type: "rapidoc",
path: "/docs"
}
Integration with Validation
Balda automatically integrates your validation schemas with Swagger documentation:
import z from 'zod';
const CreateUserSchema = z.object({
email: z.string().email(),
name: z.string().min(1),
password: z.string().min(8)
});
@post('/', {
validate: { body: CreateUserSchema },
swagger: {
name: "Create User",
requestBody: CreateUserSchema,
responses: {
"201": { $ref: "#/components/schemas/User" },
"400": { $ref: "#/components/schemas/ValidationError" }
}
}
})
async createUser(req: Request, res: Response) {
// The validation schema automatically becomes the OpenAPI schema
const user = await createUser(req.body);
res.created(user);
}
Accessing Documentation
Once configured, your API documentation will be available at:
- Swagger UI:
http://localhost:3000/docs - OpenAPI JSON:
http://localhost:3000/docs/json
The JSON specification can be used with other OpenAPI tools or imported into external documentation systems.
Best Practices
- Use Descriptive Names: Give your routes meaningful names and descriptions
- Group Related Routes: Use the
serviceproperty to group related endpoints - Document All Responses: Include success and error responses
- Use Reusable Models: Define models in the global configuration for reuse
- Keep Security Simple: Use standard security schemes when possible
- Test Your Documentation: Use the generated documentation to test your API