DEV Community

Cover image for Stop Writing API Docs Manually: Automate OpenAPI in Next.js πŸš€
Mariusz W
Mariusz W

Posted on

Stop Writing API Docs Manually: Automate OpenAPI in Next.js πŸš€

The Problem: Documentation Drift πŸ“‰

If you're building APIs with Next.js, you've probably faced this dilemma: your API documentation is always out of sync with your actual code.

You update an endpoint, add a new field to the schema, change a validation rule... and forget to update the Swagger/OpenAPI file. Two weeks later, the frontend team is frustrated because the API doesn't behave as documented.

Sound familiar?

After dealing with this exact problem across multiple projects, I built next-openapi-gen. It's a tool designed specifically for the Next.js App Router that treats your code as the single source of truth.

It recently hit 5,000 weekly downloads on npm, so I want to share how it works and how it can save you hours of manual work.

The Solution: Code as Documentation πŸ› οΈ

The core idea is simple: You are already writing types (TypeScript) or schemas (Zod) to validate your data. Why write the documentation separately?

With next-openapi-gen, your route handler is your documentation.

Here is what a fully documented endpoint looks like. Notice how standard JSDoc comments interact with Zod schemas:

// src/app/api/users/[id]/route.ts
import { NextRequest, NextResponse } from "next/server";
import { z } from "zod";

// 1. Define your schemas (Single source of truth)
export const UserParams = z.object({
  id: z.string().uuid().describe("User ID"),
});

export const UserResponse = z.object({
  id: z.string().uuid().describe("User ID"),
  name: z.string().describe("Full name"),
  email: z.string().email().describe("Email address"),
  createdAt: z.date().describe("Account creation date"),
});

/**
 * Get user by ID
 * @description Retrieves detailed user information
 * @pathParams UserParams
 * @response UserResponse
 * @auth bearer
 * @openapi
 */
export async function GET(
  request: NextRequest,
  { params }: { params: { id: string } }
) {
  // Your logic here...
  return NextResponse.json({ ... });
}
Enter fullscreen mode Exit fullscreen mode

That's it. No separate YAML files. No context switching.

Quick Start (< 2 minutes) ⚑

1. Install the package

npm install next-openapi-gen --save-dev
Enter fullscreen mode Exit fullscreen mode

2. Initialize

Run the interactive init command. This sets up the configuration and creates a documentation page for you.

npx next-openapi-gen init
Enter fullscreen mode Exit fullscreen mode

3. Generate

npx next-openapi-gen generate
Enter fullscreen mode Exit fullscreen mode

Now, just visit http://localhost:3000/api-docs and you'll see a beautiful, interactive documentation page (powered by Scalar, Swagger, or Redoc β€” your choice!).

Key Features That Make It Powerful πŸ’ͺ

1. Multiple Schema Types Support πŸ†•

You aren't locked into one approach. You can mix and match schemas, which is perfect for gradual migrations or complex projects.

{
  "schemaType": ["zod", "typescript"],
  "schemaFiles": ["./external-api.yaml"]
}
Enter fullscreen mode Exit fullscreen mode
  • Gradual Migration: Moving from TS types to Zod? Use both.
  • External Specs: Have a legacy Protobuf or external OpenAPI file? Merge it in.

2. Drizzle-Zod Integration πŸ—„οΈ

If you are using Drizzle ORM, this is a game changer. You can generate schemas directly from your database definitions.

One definition handles your Database, Validation, and Documentation.

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

// Generate Zod schema from DB table
export const CreatePost = createInsertSchema(posts, {
  title: (schema) => schema.title.min(5).max(255),
  content: (schema) => schema.content.min(10),
});

/**
 * Create a blog post
 * @body CreatePost
 * @response 201:PostResponse
 * @openapi
 */
export async function POST(request: NextRequest) {
   // ...
}
Enter fullscreen mode Exit fullscreen mode

3. Response Sets (DRY Error Handling) ♻️

Stop repeating 400, 401, 500 error definitions on every single route. Define them once in next.openapi.json:

{
  "defaultResponseSet": "common",
  "responseSets": {
    "common": ["400", "500"],
    "auth": ["400", "401", "403", "500"]
  }
}
Enter fullscreen mode Exit fullscreen mode

Now, every endpoint automatically inherits these response codes!

4. Choose Your UI 🎨

We support the best API documentation interfaces out of the box:

  • Scalar (Modern, default)
  • Swagger UI (Classic)
  • Redoc (Clean)
  • Stoplight Elements
  • RapiDoc

Real-World Example: A Complete Endpoint

Let's look at how you might handle a real-world POST request with validation and custom responses.

// src/app/api/posts/route.ts
import { NextRequest, NextResponse } from "next/server";
import { z } from "zod";

// Define schemas
const CreatePostRequest = z.object({
  title: z.string().min(5).max(255).describe("Post title"),
  content: z.string().min(50).describe("Post content with markdown support"),
  tags: z.array(z.string()).optional()
});

const PostResponse = CreatePostRequest.extend({
  id: z.string().uuid(),
  createdAt: z.string().datetime()
});

/**
 * Create new article
 * @description Creates a new blog post and notifies subscribers
 * @body CreatePostRequest
 * @response 201:PostResponse:Returns the created post
 * @add 409:ConflictError
 * @responseSet auth
 * @tag Blog
 * @openapi
 */
export async function POST(request: NextRequest) {
  const body = await request.json();

  // Validate
  const payload = CreatePostRequest.parse(body);

  // Logic...

  return NextResponse.json({ ...payload, id: "123", createdAt: new Date().toISOString() }, { status: 201 });
}
Enter fullscreen mode Exit fullscreen mode

Running npx next-openapi-gen generate parses the Zod schema (including the .min(5) validation logic), reads the JSDoc description, picks up the auth response set, and builds the full OpenAPI 3.0 spec.

Wrapping Up

Documentation shouldn't be a chore. By integrating it into your existing workflow with Zod and TypeScript, you ensure it stays accurate without extra effort.

Give it a try in your next project!

πŸ”— GitHub: https://github.com/tazo90/next-openapi-gen
πŸ“¦ NPM: npm i next-openapi-gen

If you find it useful, a star on GitHub is always appreciated! ⭐

Happy coding!

Top comments (0)