Skip to content

json-choi/restdocs

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

26 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

@restdocs

Stop writing API docs twice. Test-driven documentation for Next.js - Generate OpenAPI specs, TypeScript types, and interactive Swagger UI directly from your tests.

@restdocs/core @restdocs/nextjs License: MIT TypeScript Node.js GitHub stars GitHub issues Build Status


๐Ÿšจ The Problem: API Documentation Hell

If you're building Next.js APIs for production, you know the pain:

// โŒ THE OLD WAY: Write tests...
it('creates user', async () => {
    const res = await POST(request);
    expect(res.status).toBe(201);
});

// ...then SEPARATELY maintain OpenAPI docs
/**
 * @openapi
 * /api/users:
 *   post:
 *     summary: Create user
 *     requestBody:
 *       content:
 *         application/json:
 *           schema:
 *             type: object
 *             properties:
 *               email: {type: string, format: email}
 *               name: {type: string, minLength: 2}
 *     responses:
 *       201:
 *         description: User created
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               properties:
 *                 id: {type: string, format: uuid}
 *                 email: {type: string}
 *                 name: {type: string}
 */

Result?

  • โŒ 150+ lines of docs for 10 lines of test code
  • โŒ Docs drift within weeks (guaranteed)
  • โŒ Maintenance nightmare - update both on every API change
  • โŒ No TypeScript types generated from specs

โœ… The Solution: @restdocs

Write your test once. Get everything else automatically.

// โœ… THE @restdocs WAY: One test, complete documentation
import { api, field } from '@restdocs/core';

it('creates user', async () => {
    const res = await POST(request);

    // ๐Ÿ‘‡ This ONE call generates OpenAPI + TypeScript + Swagger UI
    api.document('POST /api/users', {
        description: 'Create a new user',
        request: {
            email: field.email().required(),
            name: field.string().required().minLength(2).maxLength(50),
        },
        response: {
            id: field.uuid(),
            email: field.email(),
            name: field.string(),
            createdAt: field.datetime(),
        },
        statusCode: 201,
    });

    expect(res.status).toBe(201);
});

After running npm test, you get:

  • โœ… openapi.json - Full OpenAPI 3.0 spec
  • โœ… types.ts - Auto-generated TypeScript interfaces
  • โœ… api.md - Human-readable documentation
  • โœ… Swagger UI - Interactive API explorer at /api/__restdocs

70% less code. 100% accuracy. Always in sync.


๐ŸŽฏ Why Teams Choose @restdocs

For Next.js 15 App Router Projects

Pain Point Traditional Approach @restdocs
API Documentation Manual OpenAPI YAML/JSON Auto-generated from tests
Type Safety Manually sync types with docs Types generated automatically
Doc Accuracy Drifts after 2-3 sprints Always matches code (test-driven)
Developer Experience Context switch to write docs Document while testing
Team Onboarding Read outdated docs โ†’ confusion Interactive Swagger UI + accurate specs
API Contract Validation Manual or separate tools Built-in validation against schemas

Real Impact

  • 70% less documentation code - One test vs. test + separate docs
  • Zero documentation drift - Docs generated from actual API behavior
  • 10x faster onboarding - New devs explore APIs via Swagger UI
  • Type-safe integration - Frontend teams use generated TypeScript types

๐Ÿ“ฆ Quick Start (5 minutes)

1. Install

npm install @restdocs/nextjs --save-dev

2. Configure Next.js

// next.config.ts
import { withRestDocs } from '@restdocs/nextjs/config';

export default withRestDocs({
    restdocs: {
        enabled: process.env.NODE_ENV === 'development',
        path: '/api/__restdocs',
    },
});

3. Configure Jest

// jest.config.js
const nextJest = require('next/jest');
const createJestConfig = nextJest({ dir: './' });

module.exports = createJestConfig({
    reporters: [
        'default',
        [
            '@restdocs/jest/reporter',
            {
                outputDir: './docs/api',
                formats: ['openapi', 'markdown', 'typescript'],
            },
        ],
    ],
});

4. Create Dev UI Route

// app/api/__restdocs/route.ts
import { createDevUIHandler } from '@restdocs/nextjs/dev-ui';

const handler = createDevUIHandler();
export { handler as GET };

5. Document Your First API

// __tests__/api/users.test.ts
import { api, field } from '@restdocs/core';
import { POST } from '@/app/api/users/route';

it('creates user', async () => {
    const res = await POST(request);

    api.document('POST /api/users', {
        description: 'Create a new user',
        request: {
            email: field.email().required(),
            name: field.string().required().minLength(2),
        },
        response: {
            id: field.uuid(),
            email: field.email(),
            name: field.string(),
        },
        statusCode: 201,
    });

    expect(res.status).toBe(201);
});

6. Run Tests & View Docs

# Generate documentation
npm test

# Start dev server
npm run dev

# Open Swagger UI
open http://localhost:3000/api/__restdocs

๐Ÿ”ฅ Key Features

Fluent Schema DSL

field
    .string()
    .required()
    .minLength(2)
    .maxLength(50)
    .pattern(/^[A-Z]/)
    .description('User name')
    .example('John Doe');

field.email().required();
field.uuid();
field.datetime();
field.integer().min(18).max(120);
field.array(field.string()).uniqueItems();
field.object({
    name: field.string(),
    age: field.integer().optional(),
});

Automatic Type Inference

const requestBody = {
    email: 'user@example.com',
    name: 'John Doe',
};

api.document('POST /api/users', {
    request: api.infer(requestBody, { required: ['email', 'name'] }),
    response: api.infer(response.body),
});

Built-in Validation

// Validate response against documented schema
api.validate(response.body).against('POST /api/users', 201);
// Throws descriptive error if validation fails

Multiple Output Formats

After running tests, get:

  • openapi.json - OpenAPI 3.0 specification
  • api.md - Markdown documentation
  • types.ts - TypeScript type definitions
  • Dev UI - Interactive Swagger UI (Next.js only)

๐Ÿงช Experimental Status & Transparency

@restdocs is in active beta (v0.3.0). This means:

โœ… What's Stable

  • Core DSL API and field builders
  • Jest integration and test reporter
  • OpenAPI 3.0 generation
  • Next.js 15 App Router support
  • TypeScript type generation
  • 247 tests passing (100% coverage)

โš ๏ธ What's Experimental

  • Performance at scale - Tested with ~50 endpoints, not 500+
  • Complex schemas - Nested objects work, but edge cases may exist
  • Framework support - Next.js is solid, others (Express/Fastify) are lighter
  • Production readiness - Use in dev/staging first, production at your own risk

๐Ÿ”ฎ What's Coming (Roadmap)

  • Vitest native support (currently uses Jest API)
  • Advanced schema features (discriminated unions, recursive types)
  • Performance optimization for large APIs (100+ endpoints)
  • Plugin ecosystem for custom generators
  • Fastify/Express deep integration

๐Ÿ’ก Perfect For

  • โœ… Greenfield Next.js projects building APIs
  • โœ… Teams tired of doc-test maintenance overhead
  • โœ… Developers who want to experiment with test-driven docs
  • โœ… Projects where API contract accuracy is critical

๐Ÿšซ Not Ready For (Yet)

  • โŒ Mission-critical APIs without fallback documentation
  • โŒ Large-scale production deployments (test in staging first)
  • โŒ Complex OpenAPI 3.1 features (allOf/oneOf/discriminators)

We're being transparent: This tool will save you time, but it's young. Use it, break it, and help us make it production-ready. Star the repo, report issues, and join the journey.


๐Ÿ—๏ธ Project Structure

@restdocs/
โ”œโ”€โ”€ packages/
โ”‚   โ”œโ”€โ”€ restdocs/      # @restdocs/core - Core DSL engine
โ”‚   โ”œโ”€โ”€ generators/    # @restdocs/generators - Output generators
โ”‚   โ”œโ”€โ”€ jest/          # @restdocs/jest - Jest integration
โ”‚   โ””โ”€โ”€ nextjs/        # @restdocs/nextjs - Next.js integration
โ””โ”€โ”€ examples/
    โ”œโ”€โ”€ express-jest/       # Express + Jest example
    โ””โ”€โ”€ nextjs-app-router/  # Next.js 15 App Router example

๐Ÿ“– Full Documentation

Field Types

Strings

field.string().required().minLength(2).maxLength(50);
field.email(); // String with email format
field.uuid(); // String with UUID format
field.uri(); // String with URI format
field.datetime(); // ISO 8601 datetime
field.date(); // ISO 8601 date

Numbers

field.number().min(0).max(100);
field.integer().multipleOf(5);

Complex Types

field.boolean().default(true);
field.object({
    name: field.string().required(),
    age: field.integer().optional(),
});
field.array(field.string()).minItems(1).maxItems(10);

API Functions

// Document an endpoint
api.document('POST /api/users', { ... });

// Auto-infer schema from data
api.infer(data, { required: ['email'] });

// Validate response against schema
api.validate(response.body).against('POST /api/users', 201);

๐ŸŽ“ Learn by Example

Check out the Next.js 15 example project with:

  • JWT authentication (register + login)
  • Users & Posts CRUD APIs
  • 19 comprehensive tests
  • Full OpenAPI documentation
  • TypeScript types generated

๐Ÿค Contributing

@restdocs is experimental - we need your feedback!


๐Ÿ™ Inspired By

  • Spring REST Docs - Test-driven documentation for Spring
  • Zod - TypeScript-first schema validation

๐Ÿ“ License

MIT ยฉ Jae Song Choi


๐Ÿ“ฎ Get Help


Built with โค๏ธ for Next.js teams who hate writing docs twice.

โญ Star this repo if you believe API docs should just work.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 3

  •  
  •  
  •