Skip to content

OpenAPI Plugin

Maintaining separate documentation (like a Postman collection or a manually edited YAML file) is a productivity killer. It inevitably drifts out of sync with the actual code.

Karin follows a Code-First philosophy for API documentation. You define your API contract using TypeScript Classes (DTOs) and Decorators, and the OpenApiPlugin automatically generates a comprehensive OpenAPI 3.0 Specification (formerly Swagger).

Concept: The Living Map

Manually writing docs is like drawing a map of a forest from memory. Code-First documentation is like a satellite updating the map in real-time. If you change a route in the code, the map updates immediately.


Metadata Reflection & Generation

The plugin works by scanning the application's metadata at bootstrap. It inspects:

  1. Router: Extracts paths (/users), methods (GET, POST), and parameters.
  2. DTOs: Converts Zod schemas into JSON Schema objects.
  3. Decorators: Reads explicit @ApiOperation, @ApiBody tags.

It then serves a Swagger UI (by default at /docs) that is interactive and executable.

Decorators Strategy

To enrich the generated documentation beyond simple paths, you decorate your controllers.

typescript
import { Controller, Post, Body } from "@project-karin/core";
import { ApiTags, ApiOperation, ApiResponse, ApiBody } from "@project-karin/openapi";
import { CreateUserDto, UserResponseDto } from "./dtos";

@Controller("users")
@ApiTags("User Management") // Groups endpoints in the UI
export class UserController {

  @Post()
  @ApiOperation({ 
    summary: "Register a new user",
    description: "Creates a user account and sends a verification email."
  })
  @ApiBody(CreateUserDto) // Links the Zod Schema to the Request Body
  @ApiResponse(201, UserResponseDto) // Links the Response Schema
  @ApiResponse(409, { description: "Email already exists" })
  async create(@Body() dto: CreateUserDto) {
    // ...
  }
}

Zod Schema Integration

The most powerful feature of the OpenApiPlugin is its native understanding of Zod. You do not need to manually define property types (type: 'string'). The plugin infers them from your validation schemas.

typescript
// create-user.dto.ts
import { z } from "zod";

export const CreateUserSchema = z.object({
  email: z.string().email().describe("The user's unique email address"),
  age: z.number().min(18).describe("Must be legal age"),
  roles: z.array(z.enum(["admin", "user"])).default(["user"])
});

// The generated OpenAPI spec will automatically include:
// - type: string, format: email
// - type: number, minimum: 18
// - enum: ["admin", "user"]
// - descriptions

Setup & Configuration

The plugin should be registered last, to ensure all other metadata has been loaded.

typescript
new OpenApiPlugin({
  title: "Karin API",
  version: "1.0.0",
  // The path where the UI will be mounted
  path: "/docs", 
  // Optional: Hide the Schema definition block in the UI to reduce clutter
  defaultModelsExpandDepth: -1, 
})

Authentication Documentation

If your API is protected, you must define the security scheme so the "Authorize" button appears in Swagger UI.

typescript
new OpenApiPlugin({
  // ...
  securitySchemes: {
    bearerAuth: {
      type: "http",
      scheme: "bearer",
      bearerFormat: "JWT",
    },
  },
  // Apply globally by default
  security: [{ bearerAuth: [] }],
})

Released under the MIT License.