Skip to main content

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 example values
  • Document all possible response codes
  • Use $ref for repeated schemas
  • Add clear description for parameters
  • Specify required fields explicitly
  • Use appropriate format for 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

Keep this cheatsheet handy while documenting your API!