Important: This documentation covers Yarn 1 (Classic).
For Yarn 2+ docs and migration guide, see yarnpkg.com.

Package detail

next-openapi-gen

tazo9015.4kMIT0.8.0TypeScript support: included

Automatically generate OpenAPI 3.0 documentation from Next.js projects, with support for Zod schemas and TypeScript types.

nextjs, openapi, swagger, typescript, zod, api-docs, api, docs, react, scalar, redoc

readme

next-openapi-gen

Automatically generate OpenAPI 3.0 documentation from Next.js projects, with support for Zod schemas and TypeScript types.

Features

  • ✅ Automatic OpenAPI documentation generation from Next.js code
  • ✅ Support for Next.js App Router (including /api/users/[id]/route.ts routes)
  • ✅ TypeScript types support
  • ✅ Zod schemas support
  • ✅ Drizzle-Zod support - Generate schemas from Drizzle ORM tables 🆕
  • ✅ JSDoc comments support
  • ✅ Multiple UI interfaces: Scalar, Swagger, Redoc, Stoplight and Rapidoc available at /api-docs url
  • ✅ Path parameters detection (/users/{id})
  • ✅ Intelligent parameter examples
  • ✅ Intuitive CLI for initialization and documentation generation

Supported interfaces

  • Scalar 🆕
  • Swagger
  • Redoc
  • Stoplight Elements
  • RapiDoc

Installation

npm install next-openapi-gen --save-dev

Quick Start

# Initialize OpenAPI configuration
npx next-openapi-gen init --ui scalar --docs-url api-docs --schema zod

# Generate OpenAPI documentation
npx next-openapi-gen generate

Configuration

During initialization (npx next-openapi-gen init), a configuration file next.openapi.json will be created in the project's root directory:

{
  "openapi": "3.0.0",
  "info": {
    "title": "Next.js API",
    "version": "1.0.0",
    "description": "API generated by next-openapi-gen"
  },
  "servers": [
    {
      "url": "http://localhost:3000",
      "description": "Local server"
    }
  ],
  "apiDir": "src/app/api",
  "schemaDir": "src/types", // or "src/schemas" for Zod schemas
  "schemaType": "zod", // or "typescript" for TypeScript types
  "outputFile": "openapi.json",
  "outputDir": "./public",
  "docsUrl": "/api-docs",
  "includeOpenApiRoutes": false,
  "ignoreRoutes": [],
  "debug": false
}

Configuration Options

Option Description
apiDir Path to the API directory
schemaDir Path to the types/schemas directory
schemaType Schema type: "zod" or "typescript"
outputFile Name of the OpenAPI output file
outputDir Directory where OpenAPI file will be generated (default: "./public")
docsUrl API documentation URL (for Swagger UI)
includeOpenApiRoutes Whether to include only routes with @openapi tag
ignoreRoutes Array of route patterns to exclude from documentation (supports wildcards)
defaultResponseSet Default error response set for all endpoints
responseSets Named sets of error response codes
errorConfig Error schema configuration
debug Enable detailed logging during generation

Documenting Your API

With Zod Schemas

// src/app/api/products/[id]/route.ts

import { NextRequest, NextResponse } from "next/server";
import { z } from "zod";

export const ProductParams = z.object({
  id: z.string().describe("Product ID"),
});

export const ProductResponse = z.object({
  id: z.string().describe("Product ID"),
  name: z.string().describe("Product name"),
  price: z.number().positive().describe("Product price"),
});

/**
 * Get product information
 * @description Fetches detailed product information by ID
 * @pathParams ProductParams
 * @response ProductResponse
 * @openapi
 */
export async function GET(
  request: NextRequest,
  { params }: { params: { id: string } }
) {
  // Implementation...
}

With TypeScript Types

// src/app/api/users/[id]/route.ts

import { NextRequest, NextResponse } from "next/server";

type UserParams = {
  id: string; // User ID
};

type UserResponse = {
  id: string; // User ID
  name: string; // Full name
  email: string; // Email address
};

/**
 * Get user information
 * @description Fetches detailed user information by ID
 * @pathParams UserParams
 * @response UserResponse
 * @openapi
 */
export async function GET(
  request: NextRequest,
  { params }: { params: { id: string } }
) {
  // Implementation...
}

With Drizzle-Zod

// src/db/schema.ts - Define your Drizzle table
import { pgTable, serial, varchar, text } from "drizzle-orm/pg-core";

export const posts = pgTable("posts", {
  id: serial("id").primaryKey(),
  title: varchar("title", { length: 255 }).notNull(),
  content: text("content").notNull(),
});

// src/schemas/post.ts - Generate Zod schema with drizzle-zod
import { createInsertSchema, createSelectSchema } from "drizzle-zod";
import { posts } from "@/db/schema";

export const CreatePostSchema = createInsertSchema(posts, {
  title: (schema) => schema.title.min(5).max(255).describe("Post title"),
  content: (schema) => schema.content.min(10).describe("Post content"),
});

export const PostResponseSchema = createSelectSchema(posts);

// src/app/api/posts/route.ts - Use in your API route
/**
 * Create a new post
 * @description Create a new blog post with Drizzle-Zod validation
 * @body CreatePostSchema
 * @response 201:PostResponseSchema
 * @openapi
 */
export async function POST(request: NextRequest) {
  const body = await request.json();
  const validated = CreatePostSchema.parse(body);
  // Implementation...
}

JSDoc Documentation Tags

Tag Description
@description Endpoint description
@pathParams Path parameters type/schema
@params Query parameters type/schema
@body Request body type/schema
@bodyDescription Request body description
@response Response type/schema with optional code and description (User, 201:User, User:Description, 201:User:Description)
@responseDescription Response description
@responseSet Override default response set (public, auth, none)
@add Add custom response codes (409:ConflictResponse, 429)
@contentType Request body content type (application/json, multipart/form-data)
@auth Authorization type (bearer, basic, apikey)
@tag Custom tag
@deprecated Marks the route as deprecated
@openapi Marks the route for inclusion in documentation (if includeOpenApiRoutes is enabled)
@ignore Excludes the route from OpenAPI documentation

CLI Usage

1. Initialization

npx next-openapi-gen init

This command will generate following elements:

  • Generate next.openapi.json configuration file
  • Set up Scalar UI for documentation display
  • Add /api-docs page to display OpenAPI documentation
  • Configure zod as the default schema tool

2. Generate Documentation

npx next-openapi-gen generate

This command will generate OpenAPI documentation based on your API code:

  • Scan API directories for routes
  • Analyze types/schemas
  • Generate OpenAPI file (openapi.json) in specified output directory (default: public folder)
  • Create Scalar/Swagger UI endpoint and page (if enabled)

3. View API Documentation

To see API documenation go to http://localhost:3000/api-docs

Examples

Path Parameters

// src/app/api/users/[id]/route.ts

// Zod
const UserParams = z.object({
  id: z.string().describe("User ID"),
});

// Or TypeScript
type UserParams = {
  id: string; // User ID
};

/**
 * @pathParams UserParams
 */
export async function GET() {
  // ...
}

Query Parameters

// src/app/api/users/route.ts

// Zod
const UsersQueryParams = z.object({
  page: z.number().optional().describe("Page number"),
  limit: z.number().optional().describe("Results per page"),
  search: z.string().optional().describe("Search phrase"),
});

// Or TypeScript
type UsersQueryParams = {
  page?: number; // Page number
  limit?: number; // Results per page
  search?: string; // Search phrase
};

/**
 * @params UsersQueryParams
 */
export async function GET() {
  // ...
}

Request Body

// src/app/api/users/route.ts

// Zod
const CreateUserBody = z.object({
  name: z.string().describe("Full name"),
  email: z.string().email().describe("Email address"),
  password: z.string().min(8).describe("Password"),
});

// Or TypeScript
type CreateUserBody = {
  name: string; // Full name
  email: string; // Email address
  password: string; // Password
};

/**
 * @body CreateUserBody
 * @bodyDescription User registration data including email and password
 */
export async function POST() {
  // ...
}

Response

// src/app/api/users/route.ts

// Zod
const UserResponse = z.object({
  id: z.string().describe("User ID"),
  name: z.string().describe("Full name"),
  email: z.string().email().describe("Email address"),
  createdAt: z.date().describe("Creation date"),
});

// Or TypeScript
type UserResponse = {
  id: string; // User ID
  name: string; // Full name
  email: string; // Email address
  createdAt: Date; // Creation date
};

/**
 * @response UserResponse
 * @responseDescription Returns newly created user object
 */
export async function GET() {
  // ...
}

// Alternative formats with inline description
/**
 * @response UserResponse:Returns user profile data
 */
export async function GET() {
  // ...
}

/**
 * @response 201:UserResponse:Returns newly created user
 */
export async function POST() {
  // ...
}

/**
 * @response 204:Empty:User successfully deleted
 */
export async function DELETE() {
  // ...
}

Authorization

// src/app/api/protected/route.ts

/**
 * @auth bearer
 */
export async function GET() {
  // ...
}

Deprecated

// src/app/api/v1/route.ts

// Zod
const UserSchema = z.object({
  id: z.string(),
  name: z.string(),
  fullName: z.string().optional().describe("@deprecated Use name instead"),
  email: z.string().email(),
});

// Or TypeScript
type UserResponse = {
  id: string;
  name: string;
  /** @deprecated Use firstName and lastName instead */
  fullName?: string;
  email: string;
};

/**
 * @body UserSchema
 * @response UserResponse
 */
export async function GET() {
  // ...
}

File Uploads / Multipart Form Data

// src/app/api/upload/route.ts

// Zod
const FileUploadSchema = z.object({
  file: z.custom<File>().describe("Image file (PNG/JPG)"),
  description: z.string().optional().describe("File description"),
  category: z.string().describe("File category"),
});

// Or TypeScript
type FileUploadFormData = {
  file: File;
  description?: string;
  category: string;
};

/**
 * @body FileUploadSchema
 * @contentType multipart/form-data
 */
export async function POST() {
  // ...
}

Response Management

Zero Config + Response Sets

Configure reusable error sets in next.openapi.json:

{
  "defaultResponseSet": "common",
  "responseSets": {
    "common": ["400", "401", "500"],
    "public": ["400", "500"],
    "auth": ["400", "401", "403", "500"]
  }
}

Usage Examples

/**
 * Auto-default responses
 * @response UserResponse
 * @openapi
 */
export async function GET() {}
// Generates: 200:UserResponse + common errors (400, 401, 500)

/**
 * With custom description inline
 * @response UserResponse:Complete user profile information
 * @openapi
 */
export async function GET() {}
// Generates: 200:UserResponse (with custom description) + common errors

/**
 * Override response set
 * @response ProductResponse
 * @responseSet public
 * @openapi
 */
export async function GET() {}
// Generates: 200:ProductResponse + public errors (400, 500)

/**
 * Add custom responses with description
 * @response 201:UserResponse:User created successfully
 * @add 409:ConflictResponse
 * @openapi
 */
export async function POST() {}
// Generates: 201:UserResponse (with custom description) + common errors + 409:ConflictResponse

/**
 * Combine multiple sets
 * @response UserResponse
 * @responseSet auth,crud
 * @add 429:RateLimitResponse
 * @openapi
 */
export async function PUT() {}
// Combines: auth + crud errors + custom 429

Error Schema Configuration

Define consistent error schemas using templates:

{
  "defaultResponseSet": "common",
  "responseSets": {
    "common": ["400", "500"],
    "auth": ["400", "401", "403", "500"],
    "public": ["400", "500"]
  },
  "errorConfig": {
    "template": {
      "type": "object",
      "properties": {
        "error": {
          "type": "string",
          "example": "{{ERROR_MESSAGE}}"
        },
        "code": {
          "type": "string",
          "example": "{{ERROR_CODE}}"
        }
      }
    },
    "codes": {
      "400": {
        "description": "Bad Request",
        "variables": {
          "ERROR_MESSAGE": "Invalid request parameters",
          "ERROR_CODE": "BAD_REQUEST"
        }
      },
      "401": {
        "description": "Unauthorized",
        "variables": {
          "ERROR_MESSAGE": "Authentication required",
          "ERROR_CODE": "UNAUTHORIZED"
        }
      },
      "403": {
        "description": "Forbidden",
        "variables": {
          "ERROR_MESSAGE": "Access denied",
          "ERROR_CODE": "FORBIDDEN"
        }
      },
      "404": {
        "description": "Not Found",
        "variables": {
          "ERROR_MESSAGE": "Resource not found",
          "ERROR_CODE": "NOT_FOUND"
        }
      },
      "500": {
        "description": "Internal Server Error",
        "variables": {
          "ERROR_MESSAGE": "An unexpected error occurred",
          "ERROR_CODE": "INTERNAL_ERROR"
        }
      }
    }
  }
}

Ignoring Routes

You can exclude routes from OpenAPI documentation in two ways:

Using @ignore Tag

Add the @ignore tag to any route you want to exclude:

// src/app/api/internal/route.ts

/**
 * Internal route - not for documentation
 * @ignore
 */
export async function GET() {
  // This route will not appear in OpenAPI documentation
}

Using ignoreRoutes Configuration

Add patterns to your next.openapi.json configuration file to exclude multiple routes at once:

{
  "openapi": "3.0.0",
  "info": {
    "title": "Next.js API",
    "version": "1.0.0"
  },
  "apiDir": "src/app/api",
  "ignoreRoutes": ["/internal/*", "/debug", "/admin/test/*"]
}

Pattern matching supports wildcards:

  • /internal/* - Ignores all routes under /internal/
  • /debug - Ignores only the /debug route
  • /admin/*/temp - Ignores routes like /admin/users/temp, /admin/posts/temp

Advanced Usage

Automatic Path Parameter Detection

The library automatically detects path parameters and generates documentation for them:

// src/app/api/users/[id]/posts/[postId]/route.ts

// Will automatically detect 'id' and 'postId' parameters
export async function GET() {
  // ...
}

If no type/schema is provided for path parameters, a default schema will be generated.

TypeScript Generics Support

The library supports TypeScript generic types and automatically resolves them during documentation generation:

// src/app/api/llms/route.ts

import { NextResponse } from "next/server";

// Define generic response wrapper
type MyApiSuccessResponseBody<T> = T & {
  success: true;
  httpCode: string;
};

// Define specific response data
type LLMSResponse = {
  llms: Array<{
    id: string;
    name: string;
    provider: string;
    isDefault: boolean;
  }>;
};

/**
 * Get list of available LLMs
 * @description Get list of available LLMs with success wrapper
 * @response 200:MyApiSuccessResponseBody<LLMSResponse>
 * @openapi
 */
export async function GET() {
  return NextResponse.json({
    success: true,
    httpCode: "200",
    llms: [
      {
        id: "gpt-5",
        name: "GPT-5",
        provider: "OpenAI",
        isDefault: true,
      },
    ],
  });
}

Intelligent Examples

The library generates intelligent examples for parameters based on their name:

Parameter name Example
id, *Id "123" or 123
slug "example-slug"
uuid "123e4567-e89b-12d3-a456-426614174000"
email "user@example.com"
name "example-name"
date "2023-01-01"

Advanced Zod Features

The library supports advanced Zod features such as:

Validation Chains

// Zod validation chains are properly converted to OpenAPI schemas
const EmailSchema = z
  .string()
  .email()
  .min(5)
  .max(100)
  .describe("Email address");

// Converts to OpenAPI with email format, minLength and maxLength

Type Aliases with z.infer

// You can use TypeScript with Zod types
import { z } from "zod";

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

// Use z.infer to create a TypeScript type
type User = z.infer<typeof UserSchema>;

// The library will be able to recognize this schema by reference `UserSchema` or `User` type.

Drizzle-Zod Support

The library fully supports drizzle-zod for generating Zod schemas from Drizzle ORM table definitions. This provides a single source of truth for your database schema, validation, and API documentation.

Supported Functions:

  • createInsertSchema() - Generate schema for inserts
  • createSelectSchema() - Generate schema for selects
  • createUpdateSchema() - Generate schema for updates

Features:

  • ✅ Automatic field extraction from refinements
  • ✅ Validation method conversion (min, max, email, url, etc.)
  • ✅ Optional/nullable field detection
  • ✅ Intelligent type mapping based on field names
  • ✅ Full OpenAPI schema generation

Example:

import { createInsertSchema } from "drizzle-zod";
import { posts } from "@/db/schema";

export const CreatePostSchema = createInsertSchema(posts, {
  title: (schema) => schema.title.min(5).max(255),
  content: (schema) => schema.content.min(10),
  published: (schema) => schema.published.optional(),
});

See the complete Drizzle-Zod example for a full working implementation with a blog API.

Examples

This repository includes several complete example projects:

📦 Available Examples

Example Description Features
next15-app-zod Zod schemas example Users, Products, Orders API with Zod validation
next15-app-drizzle-zod Drizzle-Zod integration 🆕 Blog API with Drizzle ORM + drizzle-zod
next15-app-typescript TypeScript types API with pure TypeScript type definitions
next15-app-scalar Scalar UI Modern API documentation interface
next15-app-swagger Swagger UI Classic Swagger documentation

🚀 Running Examples

cd examples/next15-app-drizzle-zod
npm install
npm run openapi:generate
npm run dev

Visit http://localhost:3000/api-docs to see the generated documentation.

Available UI providers

Scalar Swagger Redoc Stoplight Elements RapiDoc
scalar swagger redoc stoplight rapidoc

Learn More

License

MIT