OpenAPI/Swagger Cheatsheet
Quick reference for documenting JifiJs API endpoints with OpenAPI.
Basic Template
import OpenAPIHelper from '../../utils/helpers/openapi.helper';
import { RouteDocumentation } from '../../src/types/openapi.types';
export const documentation: RouteDocumentation = OpenAPIHelper.createRouteDoc(
OpenAPIHelper.mergePaths(
// Your endpoints here
),
{ schemas: { /* Your schemas */ } },
[{ name: 'YourTag', description: 'Description' }]
);
HTTP Methods
// GET
OpenAPIHelper.GET('/path', { /* config */ })
// POST
OpenAPIHelper.POST('/path', { /* config */ })
// PUT
OpenAPIHelper.PUT('/path', { /* config */ })
// PATCH
OpenAPIHelper.PATCH('/path', { /* config */ })
// DELETE
OpenAPIHelper.DELETE('/path', { /* config */ })
Security Schemes
// API Key only
security: OpenAPIHelper.security.apiKey
// JWT Bearer only
security: OpenAPIHelper.security.bearer
// Both required
security: OpenAPIHelper.security.both
// No auth
security: []
Standard Responses
responses: {
'200': OpenAPIHelper.responses.Success, // Success
'201': OpenAPIHelper.responses.Created, // Created
'400': OpenAPIHelper.responses.BadRequest, // Bad request
'401': OpenAPIHelper.responses.Unauthorized, // Unauthorized
'403': OpenAPIHelper.responses.Forbidden, // Forbidden
'404': OpenAPIHelper.responses.NotFound, // Not found
'422': OpenAPIHelper.responses.ValidationError,// Validation error
'500': OpenAPIHelper.responses.ServerError, // Server error
}
Path Parameters
parameters: [
{
name: 'id',
in: 'path',
required: true,
description: 'Resource ID',
schema: { type: 'string', pattern: '^[0-9a-fA-F]{24}$' }
}
]
Query Parameters
parameters: [
{
name: 'page',
in: 'query',
description: 'Page number',
schema: { type: 'integer', default: 1, minimum: 1 }
},
{
name: 'limit',
in: 'query',
description: 'Items per page',
schema: { type: 'integer', default: 20, minimum: 1, maximum: 100 }
},
{
name: 'search',
in: 'query',
description: 'Search query',
schema: { type: 'string' }
},
{
name: 'status',
in: 'query',
description: 'Filter by status',
schema: {
type: 'string',
enum: ['active', 'inactive', 'pending']
}
}
]
Request Body (JSON)
requestBody: {
required: true,
content: {
'application/json': {
schema: {
type: 'object',
required: ['name', 'email'],
properties: {
name: {
type: 'string',
minLength: 2,
maxLength: 50,
example: 'John Doe'
},
email: {
type: 'string',
format: 'email',
example: 'john@example.com'
},
age: {
type: 'integer',
minimum: 18,
maximum: 120,
example: 30
}
}
}
}
}
}
Request Body (File Upload)
requestBody: {
required: true,
content: {
'multipart/form-data': {
schema: {
type: 'object',
required: ['file'],
properties: {
file: {
type: 'string',
format: 'binary',
description: 'File to upload'
},
description: {
type: 'string',
description: 'File description'
}
}
}
}
}
}
Response with Data
responses: {
'200': {
description: 'Resource retrieved successfully',
content: {
'application/json': {
schema: {
type: 'object',
properties: {
error: { type: 'boolean', example: false },
message: { type: 'string', example: 'Success' },
data: {
type: 'object',
properties: {
id: { type: 'string' },
name: { type: 'string' },
created_at: { type: 'string', format: 'date-time' }
}
}
}
}
}
}
}
}
Response with Array
responses: {
'200': {
description: 'List retrieved',
content: {
'application/json': {
schema: {
type: 'object',
properties: {
error: { type: 'boolean', example: false },
message: { type: 'string', example: 'Success' },
data: {
type: 'array',
items: {
type: 'object',
properties: {
id: { type: 'string' },
name: { type: 'string' }
}
}
}
}
}
}
}
}
}
Response with Pagination
responses: {
'200': {
description: 'Paginated list',
content: {
'application/json': {
schema: {
type: 'object',
properties: {
error: { type: 'boolean', example: false },
message: { type: 'string', example: 'Success' },
data: {
type: 'object',
properties: {
content: {
type: 'array',
items: { $ref: '#/components/schemas/YourSchema' }
},
pagination: {
type: 'object',
properties: {
total: { type: 'integer', example: 100 },
page: { type: 'integer', example: 1 },
limit: { type: 'integer', example: 20 },
totalPages: { type: 'integer', example: 5 },
hasNextPage: { type: 'boolean', example: true },
hasPrevPage: { type: 'boolean', example: false }
}
}
}
}
}
}
}
}
}
}
Shared Schema
// In components
{
schemas: {
User: {
type: 'object',
properties: {
_id: { type: 'string', pattern: '^[0-9a-fA-F]{24}$' },
email: { type: 'string', format: 'email' },
first_name: { type: 'string' },
last_name: { type: 'string' },
created_at: { type: 'string', format: 'date-time' },
updated_at: { type: 'string', format: 'date-time' },
deleted_at: { type: 'string', nullable: true }
}
}
}
}
// Reference in operation
schema: { $ref: '#/components/schemas/User' }
Data Types
// String
{ type: 'string' }
{ type: 'string', minLength: 5, maxLength: 100 }
{ type: 'string', pattern: '^[A-Z]{3}$' }
{ type: 'string', format: 'email' }
{ type: 'string', format: 'date-time' }
{ type: 'string', format: 'uri' }
{ type: 'string', format: 'uuid' }
{ type: 'string', format: 'password' }
{ type: 'string', format: 'binary' }
// Number
{ type: 'number' }
{ type: 'number', minimum: 0, maximum: 100 }
{ type: 'number', multipleOf: 0.01 }
// Integer
{ type: 'integer' }
{ type: 'integer', minimum: 1, maximum: 10 }
// Boolean
{ type: 'boolean' }
// Array
{ type: 'array', items: { type: 'string' } }
{ type: 'array', items: { $ref: '#/components/schemas/Item' } }
{ type: 'array', minItems: 1, maxItems: 10 }
// Object
{ type: 'object', properties: { /* ... */ } }
// Enum
{ type: 'string', enum: ['option1', 'option2', 'option3'] }
// Nullable
{ type: 'string', nullable: true }
// OneOf (union)
{ oneOf: [{ type: 'string' }, { type: 'number' }] }
// AllOf (composition)
{ allOf: [{ $ref: '#/components/schemas/Base' }, { type: 'object', properties: { /* ... */ } }] }
Complete Endpoint Example
OpenAPIHelper.POST('/users', {
tags: ['Users'],
summary: 'Create new user',
description: 'Register a new user account',
operationId: 'createUser',
security: OpenAPIHelper.security.apiKey,
requestBody: {
required: true,
content: {
'application/json': {
schema: {
type: 'object',
required: ['email', 'password', 'first_name', 'last_name'],
properties: {
email: {
type: 'string',
format: 'email',
example: 'user@example.com'
},
password: {
type: 'string',
format: 'password',
minLength: 8,
example: 'SecureP@ss123'
},
first_name: {
type: 'string',
minLength: 2,
maxLength: 50,
example: 'John'
},
last_name: {
type: 'string',
minLength: 2,
maxLength: 50,
example: 'Doe'
}
}
}
}
}
},
responses: {
'201': {
description: 'User created successfully',
content: {
'application/json': {
schema: {
type: 'object',
properties: {
error: { type: 'boolean', example: false },
message: { type: 'string', example: 'User created' },
data: { $ref: '#/components/schemas/User' }
}
}
}
}
},
'422': OpenAPIHelper.responses.ValidationError,
}
})
NPM Scripts
# Build OpenAPI documentation
npm run docs:build
# Watch and rebuild on changes
npm run docs:watch
# Start development server
npm run dev
# Access Swagger UI
# http://localhost:3000/api-docs
File Naming
docs/routes/
├── auth.doc.ts ✅ Correct
├── app-user.doc.ts ✅ Correct
├── admin-product.doc.ts ✅ Correct
├── users.ts ❌ Wrong - no .doc
├── product.doc.js ⚠️ Works but prefer .ts
Tags
// Define tags
[
{
name: 'Authentication',
description: 'Auth and authorization endpoints'
},
{
name: 'Users',
description: 'User management'
},
{
name: 'Products',
description: 'Product CRUD operations'
}
]
// Use in operations
{
tags: ['Products'], // Single tag
// or
tags: ['Products', 'Inventory'], // Multiple tags
}
Common Patterns
MongoDB ObjectId Pattern
{
type: 'string',
pattern: '^[0-9a-fA-F]{24}$',
example: '65abc123def456789012abcd'
}
ISO Date Pattern
{
type: 'string',
format: 'date-time',
example: '2024-01-15T10:30:00.000Z'
}
Email Pattern
{
type: 'string',
format: 'email',
example: 'user@example.com'
}
URL Pattern
{
type: 'string',
format: 'uri',
example: 'https://example.com/image.jpg'
}
Price Pattern
{
type: 'number',
minimum: 0,
multipleOf: 0.01,
example: 99.99
}
Tips
DO ✅
- Use descriptive
operationId(e.g.,createProduct) - Provide realistic
examplevalues - Document all possible response codes
- Use
$reffor repeated schemas - Add clear
descriptionfor parameters - Specify
requiredfields explicitly - Use appropriate
formatfor types - Group endpoints with
tags
DON'T ❌
- Use generic operation IDs (e.g.,
op1,endpoint2) - Leave examples empty or unrealistic
- Skip error responses
- Duplicate schema definitions
- Forget to document query parameters
- Make everything optional
- Use wrong data types
- Mix unrelated endpoints in same tag
Quick Links
- Full API Documentation Guide
- API Documentation Feature
- OpenAPI Specification
- Swagger UI Documentation
Keep this cheatsheet handy while documenting your API!