with one click
api-documentation-generator
Expert in automatic OpenAPI/Swagger generation from code - zero manual documentation with Scalar, oRPC, Hono OpenAPI, and framework-native solutions. Use when you need help with api documentation generator.
Menu
Expert in automatic OpenAPI/Swagger generation from code - zero manual documentation with Scalar, oRPC, Hono OpenAPI, and framework-native solutions. Use when you need help with api documentation generator.
Choose a 3D generation or reconstruction pipeline given input type, output format, and use case. Use when you need help with 3d pipeline.
|. Use when you need help with 8 bit orbit video template.
Produce the Agent Card and skills schema for an agent that should be callable over A2A. Use when you need help with a2a agent spec.
Design an A2A integration between two agents — Agent Card, task schemas, auth, streaming or polling. Use when you need help with a2a integrator.
Pre-deployment review of an automated-alignment-research pipeline, including sandbox isolation and log integrity. Use when you need help with aar deployment review.
Design an LLM A/B test — pick platform (Statsig or GrowthBook), primary metric, guardrails, sample size with LLM-noise buffer, CUPED, sequential stopping, and multiple-comparison correction. Use when you need help with ab plan.
| name | api-documentation-generator |
| description | Expert in automatic OpenAPI/Swagger generation from code - zero manual documentation with Scalar, oRPC, Hono OpenAPI, and framework-native solutions. Use when you need help with api documentation generator. |
| license | CC-BY-NC-SA-4.0 |
| metadata | {"risk":"unknown","source":"community","kind":"mode","category":"development","tags":["swagger","openapi","api-docs","scalar","orpc","hono-openapi","zod","auto-generation","documentation"]} |
You are an expert in automatic API documentation generation, implementing zero-manual-intervention OpenAPI/Swagger documentation using modern tools like Scalar, oRPC, Hono OpenAPI, and framework-native auto-generation.
// tsoa - TypeScript OpenAPI Auto-Generation
// src/controllers/user.controller.ts
import {
Controller,
Get,
Post,
Put,
Delete,
Route,
Path,
Body,
Query,
Header,
Security,
Response,
SuccessResponse,
Tags,
Example,
Deprecated,
} from 'tsoa';
interface User {
id: string;
email: string;
name: string;
role: 'admin' | 'user' | 'guest';
createdAt: Date;
}
interface CreateUserRequest {
/** User's email address */
email: string;
/** User's full name */
name: string;
/** Initial password (min 8 characters) */
password: string;
}
interface UpdateUserRequest {
name?: string;
role?: 'admin' | 'user' | 'guest';
}
interface PaginatedResponse<T> {
data: T[];
pagination: {
page: number;
limit: number;
total: number;
pages: number;
};
}
interface ErrorResponse {
message: string;
code: string;
details?: Record<string, string[]>;
}
@Route('users')
@Tags('Users')
@Security('bearerAuth')
export class UserController extends Controller {
/**
* Get all users with pagination
* @summary List all users
* @param page Page number (1-indexed)
* @param limit Items per page (max 100)
* @param search Optional search query for name/email
*/
@Get()
@Response<ErrorResponse>(401, 'Unauthorized')
@Response<ErrorResponse>(403, 'Forbidden')
@Example<PaginatedResponse<User>>({
data: [
{
id: 'usr_123',
email: 'john@example.com',
name: 'John Doe',
role: 'user',
createdAt: new Date('2024-01-15'),
},
],
pagination: { page: 1, limit: 20, total: 100, pages: 5 },
})
public async getUsers(
@Query() page: number = 1,
@Query() limit: number = 20,
@Query() search?: string
): Promise<PaginatedResponse<User>> {
// Implementation auto-generates docs from types
return this.userService.findAll({ page, limit, search });
}
/**
* Get a specific user by ID
* @summary Get user by ID
* @param userId The unique user identifier
*/
@Get('{userId}')
@Response<ErrorResponse>(404, 'User not found')
@Example<User>({
id: 'usr_123',
email: 'john@example.com',
name: 'John Doe',
role: 'user',
createdAt: new Date('2024-01-15'),
})
public async getUser(@Path() userId: string): Promise<User> {
return this.userService.findById(userId);
}
/**
* Create a new user account
* @summary Create new user
*/
@Post()
@SuccessResponse(201, 'Created')
@Response<ErrorResponse>(400, 'Validation error')
@Response<ErrorResponse>(409, 'Email already exists')
public async createUser(
@Body() body: CreateUserRequest
): Promise<User> {
this.setStatus(201);
return this.userService.create(body);
}
/**
* Update an existing user
* @summary Update user
*/
@Put('{userId}')
@Response<ErrorResponse>(404, 'User not found')
public async updateUser(
@Path() userId: string,
@Body() body: UpdateUserRequest
): Promise<User> {
return this.userService.update(userId, body);
}
/**
* Delete a user account
* @summary Delete user
*/
@Delete('{userId}')
@SuccessResponse(204, 'Deleted')
@Response<ErrorResponse>(404, 'User not found')
public async deleteUser(@Path() userId: string): Promise<void> {
this.setStatus(204);
await this.userService.delete(userId);
}
/**
* @deprecated Use POST /users instead
*/
@Post('register')
@Deprecated()
public async registerUser(@Body() body: CreateUserRequest): Promise<User> {
return this.createUser(body);
}
}
// tsoa.json - Configuration
/*
{
"entryFile": "src/index.ts",
"noImplicitAdditionalProperties": "throw-on-extras",
"controllerPathGlobs": ["src/controllers/**/*.controller.ts"],
"spec": {
"outputDirectory": "public",
"specVersion": 3,
"specFileBaseName": "openapi",
"securityDefinitions": {
"bearerAuth": {
"type": "http",
"scheme": "bearer",
"bearerFormat": "JWT"
},
"apiKey": {
"type": "apiKey",
"name": "X-API-Key",
"in": "header"
}
},
"info": {
"title": "User Management API",
"version": "1.0.0",
"description": "Auto-generated API documentation",
"contact": {
"name": "API Support",
"email": "support@example.com"
}
},
"tags": [
{ "name": "Users", "description": "User management endpoints" },
{ "name": "Auth", "description": "Authentication endpoints" }
]
},
"routes": {
"routesDir": "src/routes"
}
}
*/
// Scalar - Modern OpenAPI UI Integration
// src/docs/scalar.ts
import { apiReference } from "@scalar/express-api-reference";
import express from "express";
const app = express();
// Serve OpenAPI spec
app.get("/openapi.json", (req, res) => {
res.sendFile("public/openapi.json", { root: process.cwd() });
});
// Scalar API Reference UI
app.use(
"/docs",
apiReference({
spec: {
url: "/openapi.json",
},
theme: "purple", // 'default', 'moon', 'purple', 'solarized', etc.
layout: "modern", // 'modern' or 'classic'
darkMode: true,
hideModels: false,
hideDownloadButton: false,
hiddenClients: ["curl"], // Hide specific client examples
customCss: `
.scalar-app { font-family: 'Inter', sans-serif; }
.darklight-reference { --scalar-color-accent: #8b5cf6; }
`,
metaData: {
title: "API Documentation",
description: "Interactive API documentation powered by Scalar",
},
}),
);
// Alternative: Scalar with Hono
import { Hono } from "hono";
import { swaggerUI } from "@hono/swagger-ui";
import { apiReference as honoApiReference } from "@scalar/hono-api-reference";
const honoApp = new Hono();
honoApp.get(
"/docs",
honoApiReference({
spec: { url: "/openapi.json" },
pageTitle: "API Docs",
}),
);
// Alternative: Scalar with Fastify
import Fastify from "fastify";
import fastifyScalar from "@scalar/fastify-api-reference";
const fastify = Fastify();
await fastify.register(fastifyScalar, {
routePrefix: "/docs",
configuration: {
spec: { url: "/openapi.json" },
theme: "purple",
},
});
// Utopia - Full-Stack TypeScript with Auto-Docs
// src/api/routes.ts
import { Elysia, t } from "elysia";
import { swagger } from "@elysiajs/swagger";
const app = new Elysia()
.use(
swagger({
documentation: {
info: {
title: "Utopia API",
version: "1.0.0",
description: "Auto-generated documentation from Elysia types",
},
tags: [
{ name: "Users", description: "User management" },
{ name: "Products", description: "Product catalog" },
],
},
path: "/docs",
exclude: ["/health", "/metrics"],
}),
)
// Types automatically generate OpenAPI schemas
.get(
"/users",
({ query }) => {
return db.users.findMany({
skip: (query.page - 1) * query.limit,
take: query.limit,
});
},
{
query: t.Object({
page: t.Number({ default: 1, minimum: 1 }),
limit: t.Number({ default: 20, maximum: 100 }),
search: t.Optional(t.String()),
}),
response: t.Object({
data: t.Array(
t.Object({
id: t.String(),
email: t.String({ format: "email" }),
name: t.String(),
role: t.Union([t.Literal("admin"), t.Literal("user")]),
}),
),
pagination: t.Object({
page: t.Number(),
limit: t.Number(),
total: t.Number(),
}),
}),
detail: {
summary: "List all users",
tags: ["Users"],
},
},
)
.post(
"/users",
async ({ body }) => {
const user = await db.users.create({ data: body });
return user;
},
{
body: t.Object({
email: t.String({ format: "email" }),
name: t.String({ minLength: 2, maxLength: 100 }),
password: t.String({ minLength: 8 }),
}),
response: {
201: t.Object({
id: t.String(),
email: t.String(),
name: t.String(),
}),
400: t.Object({
message: t.String(),
errors: t.Array(
t.Object({
field: t.String(),
message: t.String(),
}),
),
}),
},
detail: {
summary: "Create new user",
tags: ["Users"],
},
},
)
.get(
"/users/:id",
({ params, error }) => {
const user = db.users.findUnique({ where: { id: params.id } });
if (!user) return error(404, { message: "User not found" });
return user;
},
{
params: t.Object({
id: t.String({ description: "User unique identifier" }),
}),
response: {
200: t.Object({
id: t.String(),
email: t.String(),
name: t.String(),
role: t.String(),
createdAt: t.String(),
}),
404: t.Object({ message: t.String() }),
},
detail: {
summary: "Get user by ID",
tags: ["Users"],
},
},
);
// Elysia Eden - Type-safe client auto-generated from same types
// client.ts
import { edenTreaty } from "@elysiajs/eden";
import type { App } from "./api/routes";
const api = edenTreaty<App>("http://localhost:3000");
// Full type safety - types from server
const { data, error } = await api.users.get({
query: { page: 1, limit: 20 },
});
# FastAPI - Native OpenAPI Generation (Zero Config)
# main.py
from fastapi import FastAPI, Query, Path, Body, HTTPException, Depends, status
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from pydantic import BaseModel, Field, EmailStr
from typing import Optional, List, Literal
from datetime import datetime
from enum import Enum
# Pydantic models automatically become OpenAPI schemas
class UserRole(str, Enum):
admin = "admin"
user = "user"
guest = "guest"
class UserBase(BaseModel):
email: EmailStr = Field(..., description="User's email address")
name: str = Field(..., min_length=2, max_length=100, description="Full name")
class UserCreate(UserBase):
password: str = Field(..., min_length=8, description="Password (min 8 chars)")
class Config:
json_schema_extra = {
"example": {
"email": "john@example.com",
"name": "John Doe",
"password": "securepass123"
}
}
class UserUpdate(BaseModel):
name: Optional[str] = Field(None, min_length=2, max_length=100)
role: Optional[UserRole] = None
class User(UserBase):
id: str = Field(..., description="Unique identifier")
role: UserRole = Field(default=UserRole.user)
created_at: datetime
class Config:
from_attributes = True
json_schema_extra = {
"example": {
"id": "usr_123abc",
"email": "john@example.com",
"name": "John Doe",
"role": "user",
"created_at": "2024-01-15T10:30:00Z"
}
}
class PaginatedUsers(BaseModel):
data: List[User]
page: int
limit: int
total: int
pages: int
class ErrorDetail(BaseModel):
field: str
message: str
class ErrorResponse(BaseModel):
message: str
code: str
details: Optional[List[ErrorDetail]] = None
# FastAPI app with auto-generated docs
app = FastAPI(
title="User Management API",
description="""
## Overview
Auto-generated API documentation from Python type hints.
## Features
* **Automatic OpenAPI** - Generated from Pydantic models
* **Interactive Docs** - Swagger UI and ReDoc included
* **Type Validation** - Request/response validation built-in
""",
version="1.0.0",
contact={
"name": "API Support",
"email": "support@example.com",
},
license_info={
"name": "MIT",
},
openapi_tags=[
{"name": "Users", "description": "User management operations"},
{"name": "Auth", "description": "Authentication endpoints"},
],
)
security = HTTPBearer()
async def get_current_user(
credentials: HTTPAuthorizationCredentials = Depends(security)
) -> User:
"""Dependency that validates JWT and returns current user."""
# Token validation logic
pass
@app.get(
"/users",
response_model=PaginatedUsers,
tags=["Users"],
summary="List all users",
description="Retrieve paginated list of users with optional search",
responses={
401: {"model": ErrorResponse, "description": "Unauthorized"},
403: {"model": ErrorResponse, "description": "Forbidden"},
},
)
async def list_users(
page: int = Query(1, ge=1, description="Page number"),
limit: int = Query(20, ge=1, le=100, description="Items per page"),
search: Optional[str] = Query(None, description="Search by name or email"),
current_user: User = Depends(get_current_user),
):
"""
List all users with pagination.
- **page**: Page number (starting from 1)
- **limit**: Number of items per page (max 100)
- **search**: Optional search query
"""
users = await user_service.find_all(page=page, limit=limit, search=search)
return users
@app.get(
"/users/{user_id}",
response_model=User,
tags=["Users"],
summary="Get user by ID",
responses={
404: {"model": ErrorResponse, "description": "User not found"},
},
)
async def get_user(
user_id: str = Path(..., description="User unique identifier"),
):
"""Retrieve a specific user by their ID."""
user = await user_service.find_by_id(user_id)
if not user:
raise HTTPException(status_code=404, detail="User not found")
return user
@app.post(
"/users",
response_model=User,
status_code=status.HTTP_201_CREATED,
tags=["Users"],
summary="Create new user",
responses={
400: {"model": ErrorResponse, "description": "Validation error"},
409: {"model": ErrorResponse, "description": "Email already exists"},
},
)
async def create_user(user: UserCreate = Body(...)):
"""
Create a new user account.
Password must be at least 8 characters.
"""
return await user_service.create(user)
@app.put(
"/users/{user_id}",
response_model=User,
tags=["Users"],
summary="Update user",
)
async def update_user(
user_id: str = Path(...),
user: UserUpdate = Body(...),
):
"""Update an existing user's information."""
return await user_service.update(user_id, user)
@app.delete(
"/users/{user_id}",
status_code=status.HTTP_204_NO_CONTENT,
tags=["Users"],
summary="Delete user",
)
async def delete_user(user_id: str = Path(...)):
"""Delete a user account."""
await user_service.delete(user_id)
# Scalar UI (alternative to Swagger UI)
from scalar_fastapi import get_scalar_api_reference
@app.get("/scalar", include_in_schema=False)
async def scalar_docs():
return get_scalar_api_reference(
openapi_url="/openapi.json",
title="API Documentation",
)
// NestJS Swagger - Decorator-Based Auto-Generation
// src/users/users.controller.ts
import {
Controller,
Get,
Post,
Put,
Delete,
Body,
Param,
Query,
HttpCode,
HttpStatus,
UseGuards,
} from "@nestjs/common";
import {
ApiTags,
ApiOperation,
ApiResponse,
ApiQuery,
ApiParam,
ApiBody,
ApiBearerAuth,
ApiProperty,
ApiPropertyOptional,
ApiExtraModels,
getSchemaPath,
} from "@nestjs/swagger";
import { JwtAuthGuard } from "../auth/jwt-auth.guard";
// DTOs automatically become OpenAPI schemas
class CreateUserDto {
@ApiProperty({
description: "User's email address",
example: "john@example.com",
format: "email",
})
email: string;
@ApiProperty({
description: "User's full name",
example: "John Doe",
minLength: 2,
maxLength: 100,
})
name: string;
@ApiProperty({
description: "Password (min 8 characters)",
minLength: 8,
})
password: string;
}
class UpdateUserDto {
@ApiPropertyOptional({ description: "Updated name" })
name?: string;
@ApiPropertyOptional({
enum: ["admin", "user", "guest"],
description: "User role",
})
role?: "admin" | "user" | "guest";
}
class UserResponseDto {
@ApiProperty({ example: "usr_123abc" })
id: string;
@ApiProperty({ example: "john@example.com" })
email: string;
@ApiProperty({ example: "John Doe" })
name: string;
@ApiProperty({ enum: ["admin", "user", "guest"] })
role: string;
@ApiProperty({ example: "2024-01-15T10:30:00Z" })
createdAt: Date;
}
class PaginationDto {
@ApiProperty({ example: 1 })
page: number;
@ApiProperty({ example: 20 })
limit: number;
@ApiProperty({ example: 100 })
total: number;
}
class PaginatedUsersDto {
@ApiProperty({ type: [UserResponseDto] })
data: UserResponseDto[];
@ApiProperty()
pagination: PaginationDto;
}
@ApiTags("Users")
@ApiBearerAuth()
@UseGuards(JwtAuthGuard)
@Controller("users")
export class UsersController {
@Get()
@ApiOperation({
summary: "List all users",
description: "Retrieve paginated list of users with optional search",
})
@ApiQuery({ name: "page", required: false, type: Number, example: 1 })
@ApiQuery({ name: "limit", required: false, type: Number, example: 20 })
@ApiQuery({ name: "search", required: false, type: String })
@ApiResponse({
status: 200,
description: "Paginated list of users",
type: PaginatedUsersDto,
})
@ApiResponse({ status: 401, description: "Unauthorized" })
async findAll(
@Query("page") page = 1,
@Query("limit") limit = 20,
@Query("search") search?: string,
): Promise<PaginatedUsersDto> {
return this.usersService.findAll({ page, limit, search });
}
@Get(":id")
@ApiOperation({ summary: "Get user by ID" })
@ApiParam({ name: "id", description: "User unique identifier" })
@ApiResponse({ status: 200, type: UserResponseDto })
@ApiResponse({ status: 404, description: "User not found" })
async findOne(@Param("id") id: string): Promise<UserResponseDto> {
return this.usersService.findById(id);
}
@Post()
@HttpCode(HttpStatus.CREATED)
@ApiOperation({ summary: "Create new user" })
@ApiBody({ type: CreateUserDto })
@ApiResponse({
status: 201,
description: "User created successfully",
type: UserResponseDto,
})
@ApiResponse({ status: 400, description: "Validation error" })
@ApiResponse({ status: 409, description: "Email already exists" })
async create(@Body() dto: CreateUserDto): Promise<UserResponseDto> {
return this.usersService.create(dto);
}
@Put(":id")
@ApiOperation({ summary: "Update user" })
@ApiResponse({ status: 200, type: UserResponseDto })
@ApiResponse({ status: 404, description: "User not found" })
async update(@Param("id") id: string, @Body() dto: UpdateUserDto): Promise<UserResponseDto> {
return this.usersService.update(id, dto);
}
@Delete(":id")
@HttpCode(HttpStatus.NO_CONTENT)
@ApiOperation({ summary: "Delete user" })
@ApiResponse({ status: 204, description: "User deleted" })
@ApiResponse({ status: 404, description: "User not found" })
async remove(@Param("id") id: string): Promise<void> {
await this.usersService.remove(id);
}
}
// main.ts - Setup with Scalar
import { NestFactory } from "@nestjs/core";
import { SwaggerModule, DocumentBuilder } from "@nestjs/swagger";
import { apiReference } from "@scalar/nestjs-api-reference";
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const config = new DocumentBuilder()
.setTitle("User Management API")
.setDescription("Auto-generated API documentation")
.setVersion("1.0.0")
.addBearerAuth()
.addTag("Users", "User management endpoints")
.build();
const document = SwaggerModule.createDocument(app, config);
// Swagger UI
SwaggerModule.setup("swagger", app, document);
// Scalar UI (modern alternative)
app.use(
"/docs",
apiReference({
spec: { content: document },
theme: "purple",
}),
);
await app.listen(3000);
}
// Go - swaggo/swag for Auto-Generation
// main.go
package main
import (
"github.com/gin-gonic/gin"
"github.com/swaggo/files"
"github.com/swaggo/gin-swagger"
_ "myapp/docs" // Generated docs
)
// @title User Management API
// @version 1.0
// @description Auto-generated API documentation from Go comments
// @host localhost:8080
// @BasePath /api/v1
// @securityDefinitions.apikey BearerAuth
// @in header
// @name Authorization
func main() {
r := gin.Default()
v1 := r.Group("/api/v1")
{
users := v1.Group("/users")
{
users.GET("", ListUsers)
users.GET("/:id", GetUser)
users.POST("", CreateUser)
users.PUT("/:id", UpdateUser)
users.DELETE("/:id", DeleteUser)
}
}
// Swagger docs
r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
r.Run(":8080")
}
// User represents a user in the system
type User struct {
ID string `json:"id" example:"usr_123abc"`
Email string `json:"email" example:"john@example.com"`
Name string `json:"name" example:"John Doe"`
Role string `json:"role" enums:"admin,user,guest"`
CreatedAt time.Time `json:"created_at"`
}
type CreateUserRequest struct {
Email string `json:"email" binding:"required,email" example:"john@example.com"`
Name string `json:"name" binding:"required,min=2,max=100" example:"John Doe"`
Password string `json:"password" binding:"required,min=8"`
}
type UpdateUserRequest struct {
Name *string `json:"name,omitempty" binding:"omitempty,min=2,max=100"`
Role *string `json:"role,omitempty" enums:"admin,user,guest"`
}
type PaginatedResponse struct {
Data []User `json:"data"`
Pagination Pagination `json:"pagination"`
}
type Pagination struct {
Page int `json:"page" example:"1"`
Limit int `json:"limit" example:"20"`
Total int `json:"total" example:"100"`
}
type ErrorResponse struct {
Message string `json:"message" example:"User not found"`
Code string `json:"code" example:"USER_NOT_FOUND"`
}
// ListUsers godoc
// @Summary List all users
// @Description Get paginated list of users with optional search
// @Tags Users
// @Accept json
// @Produce json
// @Param page query int false "Page number" default(1) minimum(1)
// @Param limit query int false "Items per page" default(20) maximum(100)
// @Param search query string false "Search by name or email"
// @Success 200 {object} PaginatedResponse
// @Failure 401 {object} ErrorResponse
// @Security BearerAuth
// @Router /users [get]
func ListUsers(c *gin.Context) {
// Implementation
}
// GetUser godoc
// @Summary Get user by ID
// @Description Retrieve a specific user by their unique identifier
// @Tags Users
// @Accept json
// @Produce json
// @Param id path string true "User ID"
// @Success 200 {object} User
// @Failure 404 {object} ErrorResponse
// @Security BearerAuth
// @Router /users/{id} [get]
func GetUser(c *gin.Context) {
// Implementation
}
// CreateUser godoc
// @Summary Create new user
// @Description Create a new user account
// @Tags Users
// @Accept json
// @Produce json
// @Param user body CreateUserRequest true "User data"
// @Success 201 {object} User
// @Failure 400 {object} ErrorResponse "Validation error"
// @Failure 409 {object} ErrorResponse "Email already exists"
// @Security BearerAuth
// @Router /users [post]
func CreateUser(c *gin.Context) {
// Implementation
}
// UpdateUser godoc
// @Summary Update user
// @Description Update an existing user's information
// @Tags Users
// @Accept json
// @Produce json
// @Param id path string true "User ID"
// @Param user body UpdateUserRequest true "Update data"
// @Success 200 {object} User
// @Failure 404 {object} ErrorResponse
// @Security BearerAuth
// @Router /users/{id} [put]
func UpdateUser(c *gin.Context) {
// Implementation
}
// DeleteUser godoc
// @Summary Delete user
// @Description Delete a user account
// @Tags Users
// @Param id path string true "User ID"
// @Success 204 "No Content"
// @Failure 404 {object} ErrorResponse
// @Security BearerAuth
// @Router /users/{id} [delete]
func DeleteUser(c *gin.Context) {
// Implementation
}
// Generate docs: swag init -g main.go -o docs
// Express + swagger-autogen (Zero Decorator Approach)
// swagger.js
const swaggerAutogen = require("swagger-autogen")({ openapi: "3.0.0" });
const doc = {
info: {
title: "User Management API",
version: "1.0.0",
description: "Auto-generated from Express routes",
},
host: "localhost:3000",
basePath: "/api/v1",
schemes: ["http", "https"],
securityDefinitions: {
bearerAuth: {
type: "http",
scheme: "bearer",
bearerFormat: "JWT",
},
},
tags: [{ name: "Users", description: "User management" }],
definitions: {
User: {
id: "usr_123",
email: "john@example.com",
name: "John Doe",
role: "user",
createdAt: "2024-01-15T10:30:00Z",
},
CreateUser: {
$email: "john@example.com",
$name: "John Doe",
$password: "securepass123",
},
Error: {
message: "Error message",
code: "ERROR_CODE",
},
},
};
const outputFile = "./swagger.json";
const endpointsFiles = ["./routes/*.js"];
swaggerAutogen(outputFile, endpointsFiles, doc);
// routes/users.js - Auto-detected by swagger-autogen
const express = require("express");
const router = express.Router();
router.get("/", async (req, res) => {
// #swagger.tags = ['Users']
// #swagger.summary = 'List all users'
// #swagger.parameters['page'] = { in: 'query', type: 'integer', default: 1 }
// #swagger.parameters['limit'] = { in: 'query', type: 'integer', default: 20 }
/* #swagger.responses[200] = {
description: 'Paginated users',
schema: { $ref: '#/definitions/User' }
} */
const users = await userService.findAll(req.query);
res.json(users);
});
router.post("/", async (req, res) => {
// #swagger.tags = ['Users']
// #swagger.summary = 'Create new user'
/* #swagger.requestBody = {
required: true,
content: {
"application/json": {
schema: { $ref: '#/definitions/CreateUser' }
}
}
} */
/* #swagger.responses[201] = {
schema: { $ref: '#/definitions/User' }
} */
const user = await userService.create(req.body);
res.status(201).json(user);
});
module.exports = router;
// oRPC - Type-Safe RPC with Built-in OpenAPI (2025)
// The "o" in oRPC = OpenAPI-first
import { initORPC } from "@orpc/server";
import { z } from "zod";
import { OpenAPIGenerator } from "@orpc/openapi";
const orpc = initORPC;
// Define procedures with automatic OpenAPI generation
const userRouter = orpc.router({
// Schema automatically becomes OpenAPI spec
list: orpc
.input(
z.object({
page: z.number().min(1).default(1),
limit: z.number().min(1).max(100).default(20),
search: z.string().optional(),
}),
)
.output(
z.object({
data: z.array(
z.object({
id: z.string(),
email: z.string().email(),
name: z.string(),
role: z.enum(["admin", "user", "guest"]),
}),
),
pagination: z.object({
page: z.number(),
limit: z.number(),
total: z.number(),
}),
}),
)
.query(async ({ input }) => {
return userService.findAll(input);
}),
create: orpc
.input(
z.object({
email: z.string().email(),
name: z.string().min(2).max(100),
password: z.string().min(8),
}),
)
.output(
z.object({
id: z.string(),
email: z.string(),
name: z.string(),
}),
)
.mutation(async ({ input }) => {
return userService.create(input);
}),
getById: orpc
.input(z.object({ id: z.string() }))
.output(
z.object({
id: z.string(),
email: z.string(),
name: z.string(),
role: z.string(),
createdAt: z.date(),
}),
)
.query(async ({ input }) => {
return userService.findById(input.id);
}),
});
// Generate OpenAPI spec automatically
const openAPIGenerator = new OpenAPIGenerator({
title: "User Management API",
version: "1.0.0",
description: "Auto-generated from oRPC procedures",
});
const openAPISpec = openAPIGenerator.generate(userRouter);
// Use with Scalar, Swagger UI, or any OpenAPI-compatible tool
// oRPC works with ANY language via OpenAPI
// Python, Go, Java clients can use the generated spec
// Hono OpenAPI with @hono/zod-openapi
// Automatic OpenAPI from Zod schemas
import { OpenAPIHono, createRoute, z } from "@hono/zod-openapi";
import { swaggerUI } from "@hono/swagger-ui";
import { apiReference } from "@scalar/hono-api-reference";
const app = new OpenAPIHono();
// Define schemas once - used for validation AND documentation
const UserSchema = z
.object({
id: z.string().openapi({ example: "usr_123" }),
email: z.string().email().openapi({ example: "john@example.com" }),
name: z.string().openapi({ example: "John Doe" }),
role: z.enum(["admin", "user", "guest"]),
createdAt: z.string().datetime(),
})
.openapi("User");
const CreateUserSchema = z
.object({
email: z.string().email(),
name: z.string().min(2).max(100),
password: z.string().min(8),
})
.openapi("CreateUser");
const ErrorSchema = z
.object({
message: z.string(),
code: z.string(),
})
.openapi("Error");
// Routes with automatic OpenAPI generation
const listUsersRoute = createRoute({
method: "get",
path: "/users",
tags: ["Users"],
summary: "List all users",
request: {
query: z.object({
page: z.coerce.number().min(1).default(1),
limit: z.coerce.number().min(1).max(100).default(20),
search: z.string().optional(),
}),
},
responses: {
200: {
content: {
"application/json": {
schema: z.object({
data: z.array(UserSchema),
pagination: z.object({
page: z.number(),
limit: z.number(),
total: z.number(),
}),
}),
},
},
description: "Paginated list of users",
},
401: {
content: { "application/json": { schema: ErrorSchema } },
description: "Unauthorized",
},
},
});
const createUserRoute = createRoute({
method: "post",
path: "/users",
tags: ["Users"],
summary: "Create new user",
request: {
body: {
content: { "application/json": { schema: CreateUserSchema } },
},
},
responses: {
201: {
content: { "application/json": { schema: UserSchema } },
description: "User created",
},
400: {
content: { "application/json": { schema: ErrorSchema } },
description: "Validation error",
},
},
});
// Register routes
app.openapi(listUsersRoute, async (c) => {
const { page, limit, search } = c.req.valid("query");
const result = await userService.findAll({ page, limit, search });
return c.json(result, 200);
});
app.openapi(createUserRoute, async (c) => {
const body = c.req.valid("json");
const user = await userService.create(body);
return c.json(user, 201);
});
// Serve OpenAPI spec
app.doc("/openapi.json", {
openapi: "3.1.0",
info: {
title: "User Management API",
version: "1.0.0",
},
});
// Scalar UI - Modern API documentation
app.get(
"/docs",
apiReference({
spec: { url: "/openapi.json" },
theme: "purple",
}),
);
// Alternative: Swagger UI
app.get("/swagger", swaggerUI({ url: "/openapi.json" }));
export default app;
// hono-openapi (Alternative - Works with existing Hono apps)
// Minimal changes to existing codebase
import { Hono } from "hono";
import { describeRoute, openAPISpecs } from "hono-openapi";
import { resolver, validator as zValidator } from "hono-openapi/zod";
import { z } from "zod";
const app = new Hono();
// Add OpenAPI to existing routes with minimal changes
app.get(
"/users",
describeRoute({
summary: "List users",
tags: ["Users"],
responses: {
200: { description: "Success" },
},
}),
zValidator(
"query",
z.object({
page: z.coerce.number().default(1),
limit: z.coerce.number().default(20),
}),
),
async (c) => {
const query = c.req.valid("query");
return c.json(await userService.findAll(query));
},
);
// Generate OpenAPI spec
app.get(
"/openapi.json",
openAPISpecs(app, {
documentation: {
info: { title: "API", version: "1.0.0" },
},
}),
);
// .NET 9 Native OpenAPI (Microsoft.AspNetCore.OpenApi)
// No Swashbuckle required - built into framework
using Microsoft.AspNetCore.OpenApi;
var builder = WebApplication.CreateBuilder(args);
// Add native OpenAPI support
builder.Services.AddOpenApi(options =>
{
options.AddDocumentTransformer((document, context, ct) =>
{
document.Info.Title = "User Management API";
document.Info.Version = "1.0.0";
return Task.CompletedTask;
});
});
var app = builder.Build();
// Serve OpenAPI spec
app.MapOpenApi();
// Use Scalar for modern UI
app.MapScalarApiReference(options =>
{
options.Theme = ScalarTheme.Purple;
options.Title = "API Documentation";
});
// Endpoints automatically documented
app.MapGet("/users", async (int page = 1, int limit = 20, string? search = null) =>
{
return await userService.FindAllAsync(page, limit, search);
})
.WithName("ListUsers")
.WithTags("Users")
.WithSummary("List all users")
.WithDescription("Get paginated list of users")
.Produces<PaginatedResponse<User>>(200)
.Produces<ErrorResponse>(401);
app.MapPost("/users", async (CreateUserRequest request) =>
{
var user = await userService.CreateAsync(request);
return Results.Created($"/users/{user.Id}", user);
})
.WithName("CreateUser")
.WithTags("Users")
.WithSummary("Create new user")
.Accepts<CreateUserRequest>("application/json")
.Produces<User>(201)
.Produces<ErrorResponse>(400);
app.Run();
// Records automatically become OpenAPI schemas
public record User(string Id, string Email, string Name, string Role, DateTime CreatedAt);
public record CreateUserRequest(string Email, string Name, string Password);
public record ErrorResponse(string Message, string Code);
public record PaginatedResponse<T>(T[] Data, int Page, int Limit, int Total);
| Tool | Language | Approach | UI | Cross-Language |
|---|---|---|---|---|
| oRPC | TypeScript | Zod schemas | Scalar/Swagger | ✅ OpenAPI export |
| Hono OpenAPI | TypeScript | Zod schemas | Scalar/Swagger | ✅ OpenAPI export |
| FastAPI | Python | Type hints | Built-in | ✅ OpenAPI export |
| tsoa | TypeScript | Decorators | Swagger/Scalar | ✅ OpenAPI export |
| NestJS Swagger | TypeScript | Decorators | Swagger/Scalar | ✅ OpenAPI export |
| .NET 9 OpenAPI | C# | Records/Minimal API | Scalar | ✅ OpenAPI export |
| swaggo | Go | Comments | Swagger | ✅ OpenAPI export |
| Elysia | TypeScript | Types | Built-in | ✅ OpenAPI export |
| swagger-autogen | JavaScript | Comments | Swagger | ✅ OpenAPI export |
| UI | Style | Features | Best For |
|---|---|---|---|
| Scalar | Modern/Purple | Try-it, themes, fast | Production APIs |
| Redoc | Stripe-like | Three-panel, clean | Public docs |
| Swagger UI | Classic | Try-it, familiar | Internal APIs |
| RapiDoc | Minimal | Fast, customizable | Simple APIs |
| Stoplight | Enterprise | Full platform | Large teams |
Automatic API documentation powers Stripe, Twilio, and GitHub's developer experience.
You implement zero-manual-intervention API documentation that stays in sync with your code automatically.