yumerize
v0.3.1
Published
Lightweight API documentation & API hitter for Node.js — zero-config, auto-discovers routes, works with Express, NestJS, Fastify
Maintainers
Readme
yumerize
Lightweight API documentation and API hitter for Node.js. Zero-config, auto-discovers your routes, and serves a beautiful dark-themed UI to explore and test your endpoints.
Works with Express, NestJS, Fastify, or any framework via the adapter pattern.
No YAML files. No decorators. No complex setup. Just plug it in and your docs are ready.
Why yumerize?
Setting up Swagger/OpenAPI can be tedious: YAML specs, decorator clutter, manual route registration. yumerize takes a simpler approach:
- Multi-framework — adapter pattern supports Express, NestJS, Fastify, and custom frameworks
- Auto-discovery — scans your app and finds every registered route automatically
- Optional annotations — add
yumerize.doc()to any route to enrich it with params, descriptions, and response examples - Built-in API hitter — test endpoints directly from the documentation UI in your browser
- Global auth token — hit a login endpoint once and the token is auto-saved for all subsequent requests
- Full request builder — params, headers, authorization, and all body types (raw JSON, form-data, x-www-form-urlencoded, binary)
- Auto-prefilled requests — body, query, path, and header params are automatically filled from the
examplevalues in yourdoc()annotations - Response benchmarking — documented example responses (with status codes and bodies) are shown alongside the request builder so you can compare actual responses against the agreed contract
- Request history — every sent request is logged with full request/response details for review
- DOCX export — export all documented routes as a formatted Word document with one click
- Minimal setup — 3 lines of code, no config files
Installation
npm install yumerizeNote: yumerize is framework-agnostic. For Express-based apps, Express
4.18+or5.xis required. For NestJS, install the NestJS dependencies as shown in the NestJS section.
Quick Start
import express from "express";
import yumerize from "yumerize";
const app = express();
app.use(express.json());
// 1. Initialize yumerize
const api = yumerize({
title: "My API",
version: "1.0.0",
baseUrl: "http://localhost:3000",
});
// 2. Capture middleware — place BEFORE your routes
app.use(api.capture());
// 3. Define your routes as usual
app.get("/users", (req, res) => {
res.json([{ id: 1, name: "John" }]);
});
app.post("/users", (req, res) => {
res.status(201).json({ id: 2, name: req.body.name });
});
// 4. Scan & serve — place AFTER all routes
api.scan(app);
app.use("/docs", api.serve());
app.listen(3000, () => {
console.log("Server running at http://localhost:3000");
console.log("Docs at http://localhost:3000/docs");
});That's it. Open /docs in your browser to see all your routes and test them.
Adding Documentation Details
Use api.doc() as middleware on any route to add rich metadata. This is completely optional — routes without doc() will still appear in the UI.
app.get(
"/users/:id",
api.doc({
method: "GET",
path: "/users/:id",
tag: "Users", // Groups routes in the sidebar
summary: "Get user by ID",
description: "Returns a single user object by their unique identifier.",
params: [
{
name: "id",
in: "path", // "path" | "query" | "body" | "header"
type: "number",
required: true,
description: "User ID",
example: 1,
},
],
responses: [
{ status: 200, description: "User found", body: { id: 1, name: "John" } },
{ status: 404, description: "User not found" },
],
}),
(req, res) => {
res.json({ id: req.params.id, name: "John" });
},
);Documentation UI Features
The built-in UI at /docs is a full-featured API client with three sidebar panels:
Collections
Browse all discovered routes grouped by tag. Click any route to open it in a new tab. Method and URL are locked (read-only) since they come from your code, but everything else is editable. When you open a route, the request builder is auto-prefilled from your doc() annotations:
- Query & path params — filled from each param's
examplevalue - Headers — filled from header param
examplevalues - Body — if a
bodyparam has anexample, the body editor is auto-set torawJSON with the example pretty-printed, ready to send
Response Benchmarking
When you document example responses in doc() (the responses array), they are shown in the response panel before you send a request. This lets you compare the actual response against the agreed contract:
- Each documented status code gets its own tab (e.g.
200 User found,404 Not found) - Example bodies are pretty-printed for easy reading
- A label indicates it is a benchmark example, prompting you to send a real request to compare
Once you click Send, the response panel switches to the actual response data.
Request Builder
Each open request tab has four sections:
- Params — query and path parameters with key/value/description fields. Checked rows are included in the request.
- Authorization — choose from multiple auth types:
- No Auth — no authentication header sent
- Inherit Token — uses the global token (see below), so you set it once and reuse everywhere
- Bearer Token — per-request token
- Basic Auth — username/password encoded as Base64
- API Key — custom header key/value
- Headers — custom request headers with enable/disable toggles
- Body — full body editor supporting:
none— no bodyform-data— multipart key-value fieldsx-www-form-urlencoded— URL-encoded fieldsraw— raw body with format selector (JSON, XML, Text, HTML, JavaScript)binary— file upload
Global Token
The Globals sidebar tab lets you manage a shared Bearer token:
- Hit your login endpoint with auth set to Inherit Token
- If the response (2xx) contains a token field (
access_token,accessToken,token,authToken, orid_token, including nesteddata.*), it is auto-extracted and saved - All subsequent requests with Inherit Token auth automatically include it
You can also paste a token manually in the Globals tab or clear it anytime. The token persists in localStorage across browser sessions.
History
Every sent request is logged in the History sidebar tab with a complete read-only detail view showing the full request config (params, headers, auth, body) and response (status, time, size, headers, body). Click any history item to review it. Use the Clear History button to wipe all entries.
DOCX Export
Click the Export DOCX button in the top bar to download a formatted Word document (.docx) of all your documented routes. The document includes:
- Title page with API name, description, version, and base URL
- Table of contents grouped by tag
- Per endpoint: HTTP method (color-coded), path, summary, description, parameters table, example request body, and all documented responses with example bodies
The export is served from the same docs path, so no extra setup is needed. You can also trigger it programmatically:
app.use("/docs", api.serve()); // serves UI + handles /docs/export.docx
// Or as a standalone route
app.use("/export.docx", api.exportDocx());You can also access the export directly via GET /docs/export.docx.
Configuration
Pass a config object when initializing yumerize(). All fields are optional:
const api = yumerize({
title: "My API", // Documentation title (default: "Yumerize API Docs")
version: "2.0.0", // API version (default: "1.0.0")
description: "My API...", // Short description (default: "")
baseUrl: "http://...", // Base URL shown in the UI (default: "")
path: "/docs", // Docs route path (informational only)
adapter: new MyAdapter(), // Framework adapter (default: ExpressAdapter)
});If no
adapteris provided, yumerize defaults toExpressAdapterfor backward compatibility.
API Reference
yumerize(options?)
Initializes yumerize. Accepts a config object with an optional adapter field. Returns a YumerizeInstance.
// Express (default adapter)
const api = yumerize({ title: "My API" });
// NestJS
import { NestJSAdapter } from "yumerize/adapters";
const api = yumerize({ title: "My API", adapter: new NestJSAdapter() });
// Custom
const api = yumerize({ title: "My API", adapter: new MyAdapter() });api.capture()
Returns middleware that captures response data. Must be placed before your routes.
app.use(api.capture());api.doc(meta)
Returns route middleware that annotates the endpoint with documentation metadata. Place it before your handler.
YumerizeEndpointMeta fields:
| Field | Type | Required | Description |
| ------------- | ------------------------ | -------- | --------------------------------- |
| method | string | Yes | HTTP method (GET, POST, etc.) |
| path | string | Yes | Route path (/users/:id) |
| tag | string | No | Group name shown in the sidebar |
| summary | string | No | Short one-line description |
| description | string | No | Longer explanation |
| params | YumerizeParam[] | No | Parameter definitions |
| responses | YumerizeResponse[] | No | Expected response examples |
| headers | Record<string, string> | No | Custom response headers |
YumerizeParam fields:
| Field | Type | Required | Description |
| ------------- | --------- | -------- | --------------------------------------------------- |
| name | string | Yes | Parameter name |
| in | string | Yes | Location: "query", "body", "path", "header" |
| type | string | Yes | Value type (string, number, boolean, etc.) |
| required | boolean | No | Whether the parameter is required |
| description | string | No | What the parameter does |
| example | any | No | Example value shown in the UI |
YumerizeResponse fields:
| Field | Type | Required | Description |
| ------------- | -------- | -------- | ---------------------------- |
| status | number | Yes | HTTP status code |
| description | string | No | Description of this response |
| body | any | No | Example response body |
api.scan(app)
Scans the app to discover all registered routes. Call after all routes are defined. The scanning behavior depends on the adapter:
- Express/NestJS: walks the Express router tree (
app._router.stack) - Custom adapters: uses whatever mechanism the adapter implements
api.scan(app);api.serve()
Returns middleware that serves the documentation UI as HTML. Also intercepts requests to /export.docx within the mounted path to serve the DOCX export automatically.
app.use("/docs", api.serve());api.exportDocx()
Returns middleware that responds with a DOCX file containing all documented routes.
// As a standalone route
app.use("/export.docx", api.exportDocx());
// Or via the serve middleware (handled automatically)
app.use("/docs", api.serve());
// GET /docs/export.docx triggers the exportapi.getRoutes()
Returns the array of all discovered/annotated routes. Useful for testing or logging.
console.log(api.getRoutes());
// [ { method: "GET", fullPath: "/users", tag: "Users", ... }, ... ]Using with Express Router
yumerize works seamlessly with express.Router(). Define your routes in separate files and yumerize will discover them all.
routes/users.ts
import { Router, Request, Response } from "express";
const router = Router();
router.get("/", (req: Request, res: Response) => {
res.json([{ id: 1, name: "John" }]);
});
router.post("/", (req: Request, res: Response) => {
res.status(201).json({ id: 2, name: req.body.name });
});
router.get("/:id", (req: Request, res: Response) => {
res.json({ id: req.params.id, name: "John" });
});
export default router;app.ts
import express from "express";
import yumerize from "yumerize";
import usersRoute from "./routes/users";
const app = express();
app.use(express.json());
const api = yumerize({ title: "My API", baseUrl: "http://localhost:3000" });
app.use(api.capture());
// Mount routers
app.use("/users", usersRoute);
// Scan & serve
api.scan(app);
app.use("/docs", api.serve());
app.listen(3000);Routes will appear as GET /users, POST /users, GET /users/:id in the documentation.
Using with NestJS
NestJS runs on top of Express (or Fastify) by default. yumerize's NestJSAdapter accesses the underlying HTTP adapter to discover routes automatically.
// main.ts
import { NestFactory } from "@nestjs/core";
import { AppModule } from "./app.module";
import yumerize from "yumerize";
import { NestJSAdapter } from "yumerize/adapters";
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const api = yumerize({
title: "My NestJS API",
baseUrl: "http://localhost:3000",
adapter: new NestJSAdapter(),
});
app.use(api.capture()); // Capture responses
api.scan(app); // Auto-discover NestJS routes
app.use("/docs", api.serve());
await app.listen(3000);
}
bootstrap();Your NestJS controllers (@Get, @Post, etc.) will be auto-discovered and appear in the docs UI.
Using with Fastify (Custom Adapter)
Fastify has a different internal architecture, so you implement the FrameworkAdapter interface. This gives you full control:
import Fastify from "fastify";
import yumerize from "yumerize";
import { FrameworkAdapter } from "yumerize/adapters";
import { renderHtml } from "yumerize";
import { upsertRoute } from "yumerize";
class FastifyAdapter implements FrameworkAdapter {
readonly name = "fastify";
scan(_app: unknown): void {
// Fastify routes must be registered manually via route()
}
route(meta: YumerizeEndpointMeta): void {
upsertRoute({ ...meta, fullPath: meta.path });
}
capture() {
return (_req: unknown, _res: unknown, next?: unknown) => {
if (typeof next === "function") (next as () => void)();
};
}
doc(meta: YumerizeEndpointMeta) {
this.route(meta);
return [
(_req: unknown, _res: unknown, next: unknown) => {
if (typeof next === "function") (next as () => void)();
},
];
}
serve() {
return (_req: unknown, res: any) => {
res.header("Content-Type", "text/html; charset=utf-8");
res.send(renderHtml());
};
}
exportDocx() {
return async (_req: unknown, res: any) => {
const buffer = await exportDocx();
res.header("Content-Type", "application/vnd.openxmlformats-officedocument.wordprocessingml.document");
res.send(buffer);
};
}
}
const fastify = Fastify();
const api = yumerize({ adapter: new FastifyAdapter() });
fastify.get("/users", async (_req, reply) => {
api.doc({
method: "GET",
path: "/users",
tag: "Users",
summary: "List users",
});
reply.send([{ id: 1, name: "John" }]);
});
api.scan(fastify);
fastify.use("/docs", api.serve());
fastify.listen({ port: 3000 });See example/fastify/server.ts for a complete working example.
Creating a Custom Adapter
To support a framework that yumerize doesn't have a built-in adapter for, implement the FrameworkAdapter interface:
import yumerize from "yumerize";
import { FrameworkAdapter } from "yumerize/adapters";
class MyAdapter implements FrameworkAdapter {
readonly name = "my-framework";
scan(app: unknown): void {
// Walk your framework's router tree and call upsertRoute() for each route
}
capture() {
// Return middleware that intercepts responses
return (req, res, next) => {
/* ... */ next();
};
}
doc(meta) {
// Return middleware that registers route metadata
return [
(req, res, next) => {
/* register meta */ next();
},
];
}
serve() {
// Return middleware that serves the HTML docs
return (req, res) => {
res.html(renderHtml());
};
}
exportDocx() {
// Return middleware that serves the DOCX export
return async (req, res) => {
const buffer = await exportDocx();
res.send(buffer);
};
}
}
const api = yumerize({ adapter: new MyAdapter() });TypeScript
yumerize is written in TypeScript and ships with type declarations. All types are auto-resolved when you import the package.
import yumerize, {
type YumerizeInstance,
type YumerizeConfig,
type YumerizeEndpointMeta,
} from "yumerize";
import {
ExpressAdapter,
NestJSAdapter,
type FrameworkAdapter,
} from "yumerize/adapters";
// Express (default)
const api: YumerizeInstance = yumerize({
title: "My API",
version: "1.0.0",
});
// Or with an explicit adapter
const api2: YumerizeInstance = yumerize({
title: "My NestJS API",
adapter: new NestJSAdapter(),
});
// Full type safety on doc metadata
api.doc({
method: "GET",
path: "/users",
params: [
{
name: "page",
in: "query", // autocompletes: "query" | "body" | "path" | "header"
type: "number",
required: false,
},
],
});Examples
Working examples are included for multiple frameworks:
Express:
npm install
npm run build
npx ts-node example/server.ts
# Open http://localhost:3213/docsNestJS:
cd example/nestjs
npm install
npm start
# Open http://localhost:3214/docsFastify (custom adapter):
cd example/fastify
npm install
npm start
# Open http://localhost:3215/docsHow It Works
- Framework adapter — translates between yumerize's core and your framework's internals (Express, NestJS, Fastify, or custom)
api.capture()— the adapter provides middleware that wrapsres.json()to record response bodies and status codesapi.scan(app)— the adapter walks the framework's router tree to find all registered routesapi.doc()— stores rich metadata (params, descriptions, responses) alongside a routeapi.serve()— renders a single-page HTML UI with an embedded API hitter usingfetch(). Also handles DOCX export requests.api.exportDocx()— generates a formatted Word document from all documented route metadata
Testing Locally Before Publishing
If you want to test yumerize in another project before publishing to npm, use npm link:
1. Register yumerize globally (from the yumerize package directory):
cd D:\projects\yumerize
npm link2. Link it in your other project:
cd D:\path\to\your-project
npm link yumerizeNow in your project you can import it normally:
import yumerize from "yumerize";3. After making changes to yumerize source, rebuild:
cd D:\projects\yumerize
npm run buildChanges are reflected immediately in your other project since npm link creates a symlink.
4. When done testing, unlink:
# In your project
cd D:\path\to\your-project
npm unlink yumerize
# In the yumerize package
cd D:\projects\yumerize
npm unlink -gLicense
MIT
