Skip to main content

API Documentation (Swagger/OpenAPI)

JifiJs includes an automatic API documentation system using OpenAPI 3.1.0 specification and Swagger UI for interactive exploration.

Features​

  • πŸ“š OpenAPI 3.1.0 - Latest OpenAPI specification
  • 🎨 Swagger UI - Interactive API explorer
  • πŸ”„ Auto-Generation - Build from route documentation files
  • πŸ” Security Schemes - API Key and JWT Bearer authentication
  • 🏷️ Tagged Routes - Organized by functionality
  • πŸ§ͺ Try It Out - Test endpoints directly from docs
  • πŸ’Ύ Persistent Auth - Remember authentication between requests
  • ⚑ Request Duration - Display response times

Accessing the Documentation​

Once your server is running, access the API documentation at:

http://localhost:3000/api-docs

Production URL:

{YOUR_APP_URL}/api-docs

Swagger UI Features​

Interactive Interface​

The Swagger UI provides:

  • Endpoint List: All available API endpoints organized by tags
  • Request Builder: Form-based request creation
  • Response Viewer: JSON-formatted responses with syntax highlighting
  • Authentication: Built-in support for API keys and JWT tokens
  • Schema Viewer: Request/response data structures
  • Example Values: Pre-filled example data for testing

Try It Out​

  1. Click on any endpoint
  2. Click "Try it out" button
  3. Fill in required parameters
  4. Click "Execute"
  5. View the response

Authentication in Swagger​

API Key Authentication​

All endpoints require an API key:

  1. Click "Authorize" button at the top
  2. Enter your API key in x-api-key field
  3. Click "Authorize"
  4. Click "Close"

Now all requests will include the API key header.

JWT Bearer Authentication​

For authenticated endpoints:

  1. Login via /auth/login endpoint
  2. Copy the access_token from response
  3. Click "Authorize" button
  4. Paste token in bearerAuth field (without "Bearer" prefix)
  5. Click "Authorize"

All authenticated requests will now include the JWT token.

OpenAPI Document Structure​

Base Configuration​

The OpenAPI document is automatically generated with:

{
openapi: '3.1.0',
info: {
title: 'API BASE',
version: '1.0.0',
description: "API de base pour la gestion de l'authentification et des logs.",
license: {
name: 'MIT',
url: 'https://spdx.org/licenses/MIT.html'
},
contact: {
name: 'Mr NJIFANDA',
url: 'https://njifanda.com',
email: 'contact@njifanda.com'
}
},
servers: [
{
url: '{APP_URL}{ROUTE_PREFIX}',
description: 'API Server (Production/Development)'
}
]
}

Security Schemes​

Two authentication methods are defined:

1. API Key (x-api-key)​

apiKeyAuth:
type: apiKey
in: header
name: x-api-key
description: API key required for all endpoints

2. Bearer Token (JWT)​

bearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
description: JWT token for authenticated endpoints

Standard Response Formats​

All endpoints follow standardized response structures:

Success Response (200)​

{
"error": false,
"message": "Operation successful",
"data": { /* response data */ }
}

Created Response (201)​

{
"error": false,
"message": "Resource created",
"data": { /* created resource */ }
}

Error Responses​

Bad Request (400):

{
"error": true,
"message": "Bad request"
}

Unauthorized (401):

{
"error": true,
"message": "Unauthorized"
}

Forbidden (403):

{
"error": true,
"message": "Forbidden"
}

Not Found (404):

{
"error": true,
"message": "Resource not found"
}

Validation Error (422):

{
"error": true,
"message": "Validation failed",
"errors": [
{
"field": "email",
"message": "email must be a valid email"
}
]
}

Server Error (500):

{
"error": true,
"message": "Internal server error"
}

Building Documentation​

Automatic Build​

The OpenAPI document is automatically generated from route documentation files:

npm run docs:build

What it does:

  1. Scans docs/routes/ directory recursively
  2. Finds all *.doc.ts files
  3. Merges paths, schemas, and tags
  4. Generates docs/openapi.json

Watch Mode​

Auto-rebuild on changes:

npm run docs:watch

Output​

Generated file: docs/openapi.json

βœ… OpenAPI document generated successfully!
Output: /path/to/docs/openapi.json
Paths: 25
Tags: 5
Schemas: 12

Creating Route Documentation​

File Structure​

Create documentation files in docs/routes/:

docs/routes/
β”œβ”€β”€ auth.doc.ts # Authentication routes
β”œβ”€β”€ admin-user.doc.ts # Admin user routes
β”œβ”€β”€ admin-logs.doc.ts # Admin log routes
β”œβ”€β”€ app-user.doc.ts # User routes
β”œβ”€β”€ app-upload.doc.ts # Upload routes
└── your-resource.doc.ts # Your custom routes

Documentation Template​

/**
* OpenAPI Documentation for Product routes
* Corresponds to: routes/app/product.route.ts
*/

import OpenAPIHelper from '../../utils/helpers/openapi.helper';
import { RouteDocumentation } from '../../src/types/openapi.types';

export const documentation: RouteDocumentation = OpenAPIHelper.createRouteDoc(
// Paths
OpenAPIHelper.mergePaths(
// GET /product - List all products
OpenAPIHelper.GET('/product', {
tags: ['Product'],
summary: 'List all products',
description: 'Get paginated list of products',
operationId: 'listProducts',
security: OpenAPIHelper.security.both, // API Key + JWT
parameters: [
{
name: 'page',
in: 'query',
description: 'Page number',
schema: { type: 'integer', default: 1 }
},
{
name: 'limit',
in: 'query',
description: 'Items per page',
schema: { type: 'integer', default: 20 }
}
],
responses: {
'200': OpenAPIHelper.responses.Success,
'401': OpenAPIHelper.responses.Unauthorized,
}
}),

// GET /product/:id - Get single product
OpenAPIHelper.GET('/product/{id}', {
tags: ['Product'],
summary: 'Get product by ID',
description: 'Retrieve a single product by ID',
operationId: 'getProduct',
security: OpenAPIHelper.security.both,
parameters: [
{
name: 'id',
in: 'path',
required: true,
description: 'Product ID',
schema: { type: 'string' }
}
],
responses: {
'200': OpenAPIHelper.responses.Success,
'404': OpenAPIHelper.responses.NotFound,
}
}),

// POST /product - Create product
OpenAPIHelper.POST('/product', {
tags: ['Product'],
summary: 'Create new product',
description: 'Create a new product',
operationId: 'createProduct',
security: OpenAPIHelper.security.both,
requestBody: {
required: true,
content: {
'application/json': {
schema: {
type: 'object',
required: ['name', 'price'],
properties: {
name: {
type: 'string',
example: 'Product Name'
},
description: {
type: 'string',
example: 'Product description'
},
price: {
type: 'number',
example: 99.99
},
category: {
type: 'string',
example: 'Electronics'
},
}
}
}
}
},
responses: {
'201': OpenAPIHelper.responses.Created,
'422': OpenAPIHelper.responses.ValidationError,
}
}),

// PUT /product/:id - Update product
OpenAPIHelper.PUT('/product/{id}', {
tags: ['Product'],
summary: 'Update product',
description: 'Update an existing product',
operationId: 'updateProduct',
security: OpenAPIHelper.security.both,
parameters: [
{
name: 'id',
in: 'path',
required: true,
description: 'Product ID',
schema: { type: 'string' }
}
],
requestBody: {
required: true,
content: {
'application/json': {
schema: {
type: 'object',
properties: {
name: { type: 'string' },
description: { type: 'string' },
price: { type: 'number' },
category: { type: 'string' },
}
}
}
}
},
responses: {
'200': OpenAPIHelper.responses.Success,
'404': OpenAPIHelper.responses.NotFound,
'422': OpenAPIHelper.responses.ValidationError,
}
}),

// DELETE /product/:id - Delete product
OpenAPIHelper.DELETE('/product/{id}', {
tags: ['Product'],
summary: 'Delete product',
description: 'Soft delete a product',
operationId: 'deleteProduct',
security: OpenAPIHelper.security.both,
parameters: [
{
name: 'id',
in: 'path',
required: true,
description: 'Product ID',
schema: { type: 'string' }
}
],
responses: {
'200': OpenAPIHelper.responses.Success,
'404': OpenAPIHelper.responses.NotFound,
}
})
),

// Components (optional) - shared schemas
{
schemas: {
Product: {
type: 'object',
properties: {
_id: { type: 'string', example: '65abc123...' },
name: { type: 'string', example: 'Product Name' },
description: { type: 'string', example: 'Product description' },
price: { type: 'number', example: 99.99 },
category: { type: 'string', example: 'Electronics' },
created_at: { type: 'string', format: 'date-time' },
updated_at: { type: 'string', format: 'date-time' },
}
}
}
},

// Tags
[
{
name: 'Product',
description: 'Product management endpoints'
}
]
);

OpenAPIHelper Methods​

The OpenAPIHelper utility provides convenient methods:

HTTP Methods​

// GET endpoint
OpenAPIHelper.GET(path, config)

// POST endpoint
OpenAPIHelper.POST(path, config)

// PUT endpoint
OpenAPIHelper.PUT(path, config)

// DELETE endpoint
OpenAPIHelper.DELETE(path, config)

// PATCH endpoint
OpenAPIHelper.PATCH(path, config)

Security Presets​

// API Key only
security: OpenAPIHelper.security.apiKey

// JWT Bearer only
security: OpenAPIHelper.security.bearer

// Both API Key AND Bearer token required
security: OpenAPIHelper.security.both

// No authentication
security: []

Standard Responses​

OpenAPIHelper.responses.Success        // 200
OpenAPIHelper.responses.Created // 201
OpenAPIHelper.responses.BadRequest // 400
OpenAPIHelper.responses.Unauthorized // 401
OpenAPIHelper.responses.Forbidden // 403
OpenAPIHelper.responses.NotFound // 404
OpenAPIHelper.responses.ValidationError // 422
OpenAPIHelper.responses.ServerError // 500

Merge Multiple Paths​

OpenAPIHelper.mergePaths(
path1,
path2,
path3,
// ...
)

Create Complete Route Doc​

OpenAPIHelper.createRouteDoc(
paths, // Merged paths object
components, // Optional schemas/responses
tags // Array of tag objects
)

Customizing Swagger UI​

Configuration​

Edit docs/swagger.ts:

import { Express } from 'express';
import swaggerUi from 'swagger-ui-express';
import * as swaggerDocument from './openapi.json';

export default function setupSwagger(app: Express): void {
app.use(
'/api-docs',
swaggerUi.serve,
swaggerUi.setup(swaggerDocument, {
explorer: true,
customCss: '.swagger-ui .topbar { display: none }',
customSiteTitle: 'API Documentation',
swaggerOptions: {
persistAuthorization: true,
displayRequestDuration: true,
filter: true,
tryItOutEnabled: true,
},
})
);
}

Available Options​

OptionDescriptionDefault
explorerEnable search/filtertrue
customCssCustom CSS stylingHides topbar
customSiteTitleBrowser tab title'API Documentation'
persistAuthorizationRemember auth tokenstrue
displayRequestDurationShow response timetrue
filterEnable endpoint filteringtrue
tryItOutEnabledEnable "Try it out"true

Custom Styling​

Add custom CSS:

customCss: `
.swagger-ui .topbar { display: none }
.swagger-ui .info { margin: 50px 0 }
.swagger-ui .scheme-container { padding: 30px 0 }
`

Change Route​

Change the documentation URL:

app.use(
'/docs', // Changed from /api-docs
swaggerUi.serve,
swaggerUi.setup(swaggerDocument, options)
);

Updating Base Info​

Edit docs/build.ts:

const baseDocument: OpenAPIDocument = {
openapi: '3.1.0',
info: {
title: 'Your API Name', // Change title
version: '2.0.0', // Update version
description: 'Your API description',
license: {
name: 'MIT',
url: 'https://spdx.org/licenses/MIT.html',
},
contact: {
name: 'Your Name',
url: 'https://yourwebsite.com',
email: 'contact@yourwebsite.com',
},
},
servers: [
{
url: configs.getUrl() + configs.getPrefixRoutes(),
description: `${configs.getName()} - API`,
},
],
// ...
};

Tags and Organization​

Organize Endpoints by Tags​

Tags group related endpoints together:

tags: ['Authentication']  // Auth endpoints
tags: ['User'] // User management
tags: ['Product'] // Product CRUD
tags: ['Admin'] // Admin operations
tags: ['Upload'] // File uploads

Tag Descriptions​

Add tag metadata:

[
{
name: 'Authentication',
description: 'Authentication and authorization endpoints'
},
{
name: 'User',
description: 'User profile management'
},
{
name: 'Product',
description: 'Product CRUD operations'
},
]

Best Practices​

1. Descriptive Operation IDs​

Use unique, descriptive operation IDs:

operationId: 'listProducts'     // βœ… Good
operationId: 'getProducts' // ❌ Ambiguous
operationId: 'op1' // ❌ Bad

2. Complete Examples​

Provide realistic example values:

properties: {
email: {
type: 'string',
format: 'email',
example: 'user@example.com' // βœ… Good example
},
price: {
type: 'number',
example: 99.99 // βœ… Realistic value
}
}

3. Required Fields​

Always specify required fields:

schema: {
type: 'object',
required: ['name', 'email'], // βœ… Explicit requirements
properties: { /* ... */ }
}

4. Response Documentation​

Document all possible responses:

responses: {
'200': OpenAPIHelper.responses.Success,
'400': OpenAPIHelper.responses.BadRequest,
'401': OpenAPIHelper.responses.Unauthorized,
'404': OpenAPIHelper.responses.NotFound,
'422': OpenAPIHelper.responses.ValidationError,
}

5. Parameter Descriptions​

Describe all parameters:

parameters: [
{
name: 'page',
in: 'query',
description: 'Page number for pagination (1-based)', // βœ…
schema: { type: 'integer', default: 1, minimum: 1 }
}
]

6. Use Shared Schemas​

Define reusable schemas in components:

components: {
schemas: {
Product: { /* schema */ },
User: { /* schema */ },
}
}

// Reference in responses
responses: {
'200': {
content: {
'application/json': {
schema: { $ref: '#/components/schemas/Product' }
}
}
}
}

Troubleshooting​

Documentation Not Updating​

Problem: Changes not reflected in Swagger UI

Solution:

  1. Rebuild the OpenAPI document:
    npm run docs:build
  2. Restart the server:
    npm run dev
  3. Clear browser cache (Ctrl+Shift+R)

"No documentation export found"​

Problem: Warning when building docs

Solution: Ensure your .doc.ts file exports documentation:

export const documentation: RouteDocumentation = /* ... */

Endpoints Not Appearing​

Problem: New endpoints not in Swagger UI

Solution:

  1. Check file naming: must end with .doc.ts
  2. Place file in docs/routes/ directory
  3. Export documentation constant
  4. Rebuild: npm run docs:build
  5. Restart server

Authentication Not Working​

Problem: 401 errors when testing endpoints

Solution:

  1. Click Authorize button
  2. Enter valid API key
  3. For protected routes, also add JWT token
  4. Ensure tokens haven't expired
  5. Check security configuration matches backend

Exporting Documentation​

OpenAPI JSON​

The generated openapi.json can be:

  • Imported into Postman
  • Used with API clients
  • Uploaded to API documentation platforms
  • Shared with frontend developers

Download from Swagger UI​

  1. Open /api-docs
  2. Look for /api-docs/swagger.json link
  3. Download the JSON file

Use with Other Tools​

Postman:

  1. File β†’ Import
  2. Select openapi.json
  3. All endpoints imported with examples

Insomnia:

  1. Create β†’ Import From β†’ File
  2. Select openapi.json

API Clients: Generate client code using OpenAPI Generator:

npx @openapitools/openapi-generator-cli generate \
-i docs/openapi.json \
-g typescript-axios \
-o ./generated-client

Next Steps​


Questions? Check the GitHub Discussions or open an issue.