Skip to main content

Swagger API Documentation

Available from v1.3.0-beta

Arkos.js provides comprehensive OpenAPI documentation generation that now integrates seamlessly with ArkosRouter's declarative configuration. The new approach allows you to document your APIs directly through route configuration, making documentation maintenance easier and more type-safe.

Quick Start

Enable API documentation with ArkosRouter integration:

// arkos.config.ts
import { ArkosConfig } from "arkos";

const arkosConfig: ArkosConfig = {
swagger: {
mode: "prisma", // or "class-validator" or "zod"
options: {
definition: {
info: {
title: "My API",
version: "1.0.0",
description: "API documentation with ArkosRouter integration",
},
},
},
},
};

export default arkosConfig;

Document your routes declaratively:

// src/modules/user/user.router.ts
import { ArkosRouter } from "arkos";
import z from "zod";

const router = ArkosRouter();

const UserSchema = z.object({
id: z.string(),
name: z.string(),
email: z.string().email(),
});

router.get(
{
path: "/:id",
experimental: {
openapi: {
summary: "Get user by ID",
description: "Retrieve a specific user by their unique identifier",
tags: ["Users"],
responses: {
200: UserSchema, // Direct schema reference!
404: {
content: z.object({ message: z.string() }),
description: "User not found",
},
},
},
},
},
userController.getUser
);

export default router;

Start your server and visit /api/docs to see your interactive API documentation.

ArkosRouter OpenAPI Integration

The new ArkosRouter approach makes API documentation declarative and type-safe.

Basic Route Documentation

import { ArkosRouter } from "arkos";
import z from "zod";

const router = ArkosRouter();

// Simple endpoint documentation
router.get(
{
path: "/api/health",
experimental: {
openapi: {
summary: "Health check",
description: "Check if the API is running",
tags: ["System"],
responses: {
200: {
content: z.object({ status: z.string(), timestamp: z.string() }),
description: "API is healthy",
},
},
},
},
},
healthController.check
);

// With authentication and validation
const CreatePostSchema = z.object({
title: z.string().min(5),
content: z.string(),
});

router.post(
{
path: "/api/posts",
authentication: {
resource: "post",
action: "Create",
rule: ["Admin", "Editor"],
},
validation: {
body: CreatePostSchema,
},
experimental: {
openapi: {
summary: "Create a new post",
tags: ["Posts"],
responses: {
201: {
content: z.object({ id: z.string(), title: z.string() }),
description: "Post created successfully",
},
401: {
content: z.object({ message: z.string() }),
description: "Authentication required",
},
},
},
},
},
postController.create
);

Response Schema Shortcuts

One of the most powerful features is the ability to use schemas directly in responses:

import { ArkosRouter } from "arkos";
import z from "zod";

const router = ArkosRouter();

const UserSchema = z.object({
id: z.string(),
name: z.string(),
email: z.string().email(),
});

const ErrorSchema = z.object({
error: z.string(),
code: z.number(),
});

// Shortcut: Direct schema reference
router.get(
{
path: "/api/users/:id",
experimental: {
openapi: {
responses: {
200: UserSchema, // Auto-converted to OpenAPI schema!
404: ErrorSchema,
500: ErrorSchema,
},
},
},
},
userController.getUser
);

// Medium: Schema with description
router.post(
{
path: "/api/users",
experimental: {
openapi: {
responses: {
201: {
content: UserSchema,
description: "User created successfully",
},
400: {
content: ErrorSchema,
description: "Invalid input data",
},
},
},
},
},
userController.create
);

Overriding Auto-Generated Documentation

Customize the documentation for auto-generated Prisma model endpoints:

Configuring Model Endpoints

// src/modules/user/user.router.ts
import { ArkosRouter } from "arkos";
import { RouterConfig } from "arkos";
import z from "zod";

const UserResponseSchema = z.object({
id: z.string(),
name: z.string(),
email: z.string().email(),
profile: z.object({
bio: z.string().optional(),
avatar: z.string().url().optional(),
}),
});

const ErrorSchema = z.object({
message: z.string(),
code: z.string(),
});

// Configure auto-generated endpoints
export const config: RouterConfig = {
findMany: {
experimental: {
openapi: {
summary: "List users with pagination",
description: "Get a paginated list of users with their profiles",
tags: ["Users"],
responses: {
200: {
content: z.array(UserResponseSchema),
description: "Users retrieved successfully",
},
},
},
},
},

findOne: {
experimental: {
openapi: {
summary: "Get user details",
description: "Retrieve detailed information about a specific user",
tags: ["Users"],
responses: {
200: UserResponseSchema,
404: {
content: ErrorSchema,
description: "User not found",
},
},
},
},
},

createOne: {
experimental: {
openapi: {
summary: "Create new user",
description: "Create a new user account with profile information",
tags: ["Users"],
responses: {
201: UserResponseSchema,
409: {
content: ErrorSchema,
description: "Email already exists",
},
},
},
},
},
};

const router = ArkosRouter();

// Add custom endpoints to the same router
router.post(
{
path: "/:id/activate",
experimental: {
openapi: {
summary: "Activate user account",
tags: ["Users"],
responses: {
200: UserResponseSchema,
404: {
content: ErrorSchema,
description: "User not found",
},
},
},
},
},
userController.activate
);

export default router;

Authentication Endpoints Documentation

Document your authentication flows with proper security schemes:

// src/modules/auth/auth.router.ts
import { ArkosRouter, RouterConfig } from "arkos";
import z from "zod";

const LoginSchema = z.object({
email: z.string().email(),
password: z.string().min(6),
});

const LoginResponseSchema = z.object({
token: z.string(),
user: z.object({
id: z.string(),
name: z.string(),
email: z.string().email(),
role: z.string(),
}),
});

const ErrorSchema = z.object({
message: z.string(),
code: z.string(),
});

export const config: RouterConfig<"auth"> = {
login: {
validation: {
body: LoginSchema, // Or define login.schema.ts file to auto load
},
experimental: {
openapi: {
summary: "User login",
description: "Authenticate user and return JWT token",
responses: {
200: LoginResponseSchema,
401: {
content: ErrorSchema,
description: "Invalid credentials",
},
400: {
content: ErrorSchema,
description: "Validation error",
},
},
},
},
},
signup: {
validation: {
body: z.object({
name: z.string().min(2),
email: z.string().email(),
password: z.string().min(6),
}),
},
experimental: {
uploads: { type: "single", field: "photo" },
openapi: {
summary: "User Signup",
responses: {
201: LoginResponseSchema,
409: {
content: ErrorSchema,
description: "Email already registered",
},
},
},
},
},
};

const router = ArkosRouter();

export default router;

Schema Generation Modes

Arkos supports three approaches to generate OpenAPI schemas:

Prisma Mode: Live Schema Reflection

// arkos.config.ts
const arkosConfig = {
swagger: {
mode: "prisma",
options: {
definition: {
info: {
title: "My API",
version: "1.0.0",
},
},
},
},
};

export default arkosConfig;

Prisma mode automatically generates schemas from your Prisma models and syncs with Custom Prisma Query Options:

// src/modules/user/user.query.ts
import { PrismaQueryOptions } from "arkos/prisma";

const userPrismaQueryOptions: PrismaQueryOptions<UserDelegate> = {
findMany: {
select: {
id: true,
name: true,
email: true,
posts: {
select: {
id: true,
title: true,
},
},
},
},
};

export default userPrismaQueryOptions;

The generated OpenAPI schema will show User responses with exactly these fields.

Integration with Validation Schemas

Important: Don't mix validation schemas and OpenAPI requestBody|params|query definitions:

// ✅ CORRECT: Use validation.body for input schemas
router.post(
{
path: "/api/users",
validation: {
body: CreateUserSchema, // This auto-generates OpenAPI requestBody
},
experimental: {
openapi: {
summary: "Create user",
responses: {
201: UserResponseSchema, // Document responses here
},
},
},
},
userController.create
);

// ❌ INCORRECT: Redefining requestBody in openapi
router.post(
{
path: "/api/users",
validation: {
body: CreateUserSchema,
},
experimental: {
openapi: {
requestBody: {
// ← This will cause startup error!
content: CreateUserSchema,
},
responses: {
201: UserResponseSchema,
},
},
},
},
userController.create
);

// ✅ CORRECT: For endpoints without validation
router.get(
{
path: "/api/health",
experimental: {
openapi: {
responses: {
200: HealthSchema, // Only responses needed
},
},
},
},
healthController.check
);

Custom Routers Documentation

Easily add documentation to custom routers with the declarative approach:

// src/routers/analytics.router.ts
import { ArkosRouter } from "arkos";
import z from "zod";

const router = ArkosRouter();

const AnalyticsSchema = z.object({
totalUsers: z.number(),
activeUsers: z.number(),
growthRate: z.number(),
topPosts: z.array(
z.object({
id: z.string(),
title: z.string(),
views: z.number(),
})
),
});

router.get(
{
path: "/api/analytics/dashboard",
authentication: {
resource: "analytics",
action: "View",
rule: ["Admin", "Manager"],
},
experimental: {
openapi: {
summary: "Get analytics dashboard",
description: "Retrieve comprehensive analytics data",
tags: ["Analytics"],
responses: {
200: AnalyticsSchema,
403: {
content: z.object({ message: z.string() }),
description: "Insufficient permissions",
},
},
},
},
},
analyticsController.getDashboard
);

// Complex endpoint with query parameters
router.get(
{
path: "/api/analytics/reports",
validation: {
query: z.object({
startDate: z.string().datetime(),
endDate: z.string().datetime(),
metric: z.enum(["users", "posts", "revenue"]),
}),
},
experimental: {
openapi: {
summary: "Generate analytics report",
tags: ["Analytics"],
responses: {
200: z.object({
report: z.any(), // Use z.any() for complex dynamic objects
generatedAt: z.string().datetime(),
}),
400: {
content: z.object({ message: z.string() }),
description: "Invalid query parameters",
},
},
},
},
},
analyticsController.generateReport
);

export default router;

Register the custom router:

// app.ts
import arkos from "arkos";
import analyticsRouter from "./routers/analytics.router";

arkos.init({
use: [analyticsRouter],
});

Migration Guide

From JSDoc to ArkosRouter OpenAPI

import { ArkosRouter } from "arkos";
import z from "zod";

const router = ArkosRouter();

const UserSchema = z.object({
id: z.string(),
name: z.string(),
email: z.string().email(),
});

router.get(
{
path: "/api/users/:id",
experimental: {
openapi: {
summary: "Get user by ID",
tags: ["Users"],
responses: {
200: UserSchema, // Direct schema reference!
404: {
content: z.object({ message: z.string() }),
description: "User not found",
},
},
},
},
},
userController.getUser
);

Benefits of the New Approach

  1. Type Safety: Zod schemas provide compile-time validation
  2. Single Source of Truth: Schemas used for validation and documentation
  3. Better IDE Support: Auto-completion and type checking
  4. Easier Refactoring: Change schemas in one place
  5. Cleaner Code: No more large JSDoc blocks

Configuration Reference

Arkos Config

// arkos.config.ts
const arkosConfig = {
swagger: {
mode: "prisma", // "prisma" | "class-validator" | "zod"
endpoint: "/api/docs", // Documentation URL
options: {
definition: {
openapi: "3.0.0",
info: {
title: "My API",
version: "1.0.0",
},
servers: [
{
url: "http://localhost:8000",
description: "Development",
},
],
components: {
securitySchemes: {
BearerAuth: {
type: "http",
scheme: "bearer",
bearerFormat: "JWT",
},
},
},
},
},
scalarApiReferenceConfiguration: {
theme: "deepSpace",
darkMode: true,
},
},
};

export default arkosConfig;

Troubleshooting

Common Issues

  1. Startup Errors: Caused by redefining requestBody|params|query in openapi when using validation.body|query|params
  2. Missing Documentation: Ensure experimental.openapi is properly configured
  3. Schema Conflicts: Use different schemas for input (validation) and output (responses)

The new ArkosRouter OpenAPI integration provides a more maintainable, type-safe approach to API documentation that seamlessly integrates with your existing validation and authentication setup.