npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

jerant

v0.2.0

Published

Zero-config automatic API documentation for Express, Fastify, Koa and Hono. OpenAPI 3.1 + Swagger UI/Redoc/Scalar served from your own app at /docs.

Readme

jerant

Zero-config automatic API documentation for Express, Fastify, Koa and Hono.

FastAPI-style docs — OpenAPI 3.1 + a modern dark UI — served from your own app, on the same port, with no annotations required. Routes are discovered automatically, schemas are learned from your validation libraries, your Prisma schema, your source code, and real traffic.

npm install jerant
const express = require("express");
const { jerant } = require("jerant");

const app = express();
jerant(app);

app.listen(3000);

That's it:

| URL | What | | --- | --- | | http://localhost:3000 | Your application, unchanged | | http://localhost:3000/docs | Interactive docs (dark purple UI, search, try-it-out, teach mode) | | http://localhost:3000/openapi.json | OpenAPI 3.1 document |

No separate documentation server, no code generation step, no YAML.


Table of contents

  1. How your API gets documented
  2. Confidence scores & teach mode
  3. Validation libraries
  4. Prisma-aware tracing
  5. Authentication & the Authorize button
  6. Custom validation errors
  7. Response envelopes & default responses
  8. Example values
  9. Tag groups
  10. The docs UI
  11. CLI: export & breaking-change guard
  12. Schema drift detection
  13. File uploads / form-data
  14. Data safety & redaction
  15. Storage & persistence
  16. Fastify, Koa and Hono
  17. Full configuration reference
  18. Programmatic API
  19. Production notes
  20. Architecture

1. How your API gets documented

jerant picks the highest-quality schema source available for every route, automatically:

| Priority | Source | Confidence | What it needs from you | |---|---|---|---| | 1 | Validation schemas — Zod, Joi, Yup, AJV, plain JSON Schema, class-validator | 100% | a validate({...}) on the route | | 1 | express-validator chains, class-validator classes | 90% | nothing — detected automatically | | 2 | Runtime traffic analysis — real requests/responses observed live | 40–90% | just use the API | | 3 | Prisma schema tracingprisma.user.create({data: req.body}) + schema.prisma | 75% | nothing | | 4 | Static code analysis — fields your handlers read (req.body.x, {a} = req.body), status codes, upload config; includes a full project source scan that sees through wrappers, named controllers, and follows payloads down to SQL/ORM writes | 55% | nothing | | 5 | Route scanning — every route (nested routers included) appears from the moment the server starts | 25% | nothing |

Every operation shows its source and confidence in the UI and carries machine-readable metadata in x-jerant (source, confidence, observation count, drift issues).

2. Confidence scores & teach mode

Raising a score is always the same move: give jerant a better source.

  • Add validate({...}) → instant 100%
  • Handler writes into a Prisma model → 75% automatically
  • Send real requests → climbs to ~90% (saturates around 30 observations)

Teach mode closes the loop from inside the docs. Any operation below 50% confidence opens automatically with a "◌ Teach this endpoint" panel where anyone can freely enter:

  • path params — one input per {param}
  • query params — unlimited key/value rows (no declaration needed)
  • headers — e.g. an Authorization token
  • body — raw JSON, form-data (with real file uploads), or url-encoded

Hitting Send request exercises your real app; the analyzer observes the request and response, the confidence chip updates live, and a reload shows the learned schema and parameters. Once an operation crosses 50% it graduates to a normal documented endpoint. Disable with teachMode: false.

3. Validation libraries

Zod (recommended)

validate() gives you real request validation and perfect docs from one declaration:

const { z } = require("zod");
const { jerant, validate } = require("jerant");

app.post(
  "/users",
  validate({
    summary: "Create a user",
    tags: ["users"],
    body: z.object({
      name: z.string().min(1),
      email: z.string().email(),
      age: z.number().int().optional(),
    }),
    responses: { 201: z.object({ id: z.number(), name: z.string() }) },
  }),
  (req, res) => res.status(201).json({ id: 1, name: req.body.name })
);

Invalid requests get a structured error response; the docs get exact schemas. Zod v3 and v4 are both supported (including z.iso.date(), z.looseObject(), discriminated unions).

Joi, Yup, AJV, JSON Schema, class-validator

validate() accepts them all — the library is detected automatically:

validate({ body: Joi.object({ sku: Joi.string().required() }) })
validate({ body: yup.object({ email: yup.string().email().required() }) })
validate({ body: ajv.compile(schema) })      // compiled AJV validator
validate({ body: { type: "object", properties: { id: { type: "integer" } } } })

express-validator

Detected with no jerant code at all:

app.post("/subscribe", body("email").isEmail(), handler);
// documented as { email: string (format: email), required }

Documentation without validation

doc() attaches metadata and schemas without enforcing anything:

const { doc } = require("jerant");
app.get("/reports", doc({ summary: "List reports", tags: ["reports"] }), handler);

4. Prisma-aware tracing

When a handler — or the service function it calls — writes request data into a Prisma model, jerant reads schema.prisma and types the body fields from the model:

app.post("/users", async (req, res) => {
  const user = await prisma.user.create({ data: req.body });
  res.status(201).json(user);
});
// documented from schema.prisma: email (string, required), name (string,
// required), age (integer), role (enum ADMIN|MEMBER)

Server-managed columns are excluded automatically: @id, @updatedAt, and generated defaults (now(), autoincrement(), uuid()). Fields with literal defaults (@default(MEMBER)) are documented as optional. Configure with prisma: { schemaPath: "db/schema.prisma" }, disable with prisma: { enabled: false }.

5. Authentication & the Authorize button

Declare your auth model once — every operation documents it and Swagger UI shows its Authorize button:

jerant(app, { security: "bearer" });   // or "apiKey", "basic", "cookie"

Mark exceptions per route (security: [] = public):

app.post("/login", validate({ security: [] }), loginHandler);

Full control:

jerant(app, {
  security: {
    schemes: {
      bearerAuth: { type: "http", scheme: "bearer", bearerFormat: "JWT" },
      apiKeyAuth: { type: "apiKey", in: "header", name: "X-Service-Key" },
    },
    default: "bearerAuth",
  },
});

With no configuration at all, jerant auto-detects auth middleware from its source — req.headers.authorization reads, Bearer checks, passport.authenticate(...), x-api-key lookups, or middleware named requireAuth/isAuthenticated/etc. — and documents those routes as secured. Disable with security: false or security: { autoDetect: false }.

6. Custom validation errors

The default failure response is 400 {error, details}. Reshape it app-wide:

jerant(app, {
  validationError: {
    status: 422,
    formatter: ({ details }) => ({ success: false, errors: details }),
  },
});

…or per route: validate({ body: schema, error: { status: 422 } }).

7. Response envelopes & default responses

If every response ships in a wrapper, declare it once — declared response schemas slot into $data automatically:

jerant(app, {
  envelope: { success: "boolean", message: "string", data: "$data" },
  defaultResponses: {
    401: { description: "Not signed in", schema: { type: "object", properties: { error: { type: "string" } } } },
    500: { description: "Internal error" },
  },
});

defaultResponses are added to every operation that doesn't already document that status. The envelope also accepts a full JSON Schema containing a "$data" slot, plus { schema, statuses: [200, 201] } to control which statuses get wrapped (default: all 2xx).

8. Example values

Every request/response body carries an example whose values are the type names — instant, self-explanatory, and guaranteed to never leak data:

{ "email": "email", "password": "string", "age": "number", "score": "float" }

| examples option | Behavior | |---|---| | "placeholder" (default) | type-name values as above | | "captured" | real (redacted) traffic payloads | | false | no examples |

9. Tag groups

Give the sidebar groups descriptions and a fixed order:

jerant(app, {
  tags: [
    { name: "users", description: "User management" },
    { name: "orders", description: "Order processing" },
    "internal",
  ],
});

Routes without explicit tags are grouped by their first meaningful path segment (/api/v1/users/…users).

10. The docs UI

The default UI is a modern dark theme (deep purple surfaces, lavender accents) with:

  • a stats header: endpoints, tag groups, schemas, average confidence, drift issues, and endpoints still in learning mode
  • per-operation confidence chips — color-graded score with source, observation count and drift warnings on hover
  • Expand all / Collapse all, Copy spec URL, Download JSON buttons
  • / keyboard shortcut to jump to the endpoint search
  • teach panels on low-confidence operations (see §2)
  • validated color palette (contrast + colorblind-safe method badges on the dark surface)

Choosing a UI

jerant(app, { ui: "swagger" });   // default
jerant(app, { ui: "redoc" });     // Redoc, same dark purple theme
jerant(app, { ui: "scalar" });    // Scalar, purple dark mode

Self-hosting the assets (offline / strict CSP)

npm install swagger-ui-dist
jerant(app, { assets: "local" });   // served at /docs/assets/*, allowlisted

Bring your own UI

jerant(app, { ui: false });                    // serve only /openapi.json
app.get("/mydocs", (req, res) => res.sendFile("my-ui.html"));

jerant(app, { ui: { html: myHtmlString } });   // your page at docsPath

jerant(app, {                                  // or a render function
  ui: ({ openapiPath, title }) => `<html>… fetch("${openapiPath}") …</html>`,
});

Any custom UI gets the full spec including the x-jerant metadata (source, confidence, drift), so you can render your own badges.

11. CLI: export & breaking-change guard

npx jerant export server.js -o openapi.json      # CI / codegen / Postman
npx jerant diff openapi.main.json openapi.json   # exits 1 on breaking changes
npx jerant diff main-server.js server.js         # app entries work too
  • export loads your app (with JERANT_EXPORT=1 set, so you can skip listen() if you want) and writes the full OpenAPI document. --wait <ms> allows extra time for async route registration; --pretty formats stdout output.
  • diff reports breaking changes — removed operations, new required request fields/params, type changes, removed response properties — and exits 1 when any are found (--fail-on any also fails on additions). Wire it into CI to block accidental breakage.

12. Schema drift detection

When a route has a declared schema and enough observed traffic (≥5 requests), jerant compares the two and flags mismatches:

  • type-mismatch — declared number, traffic shows string
  • required-field-never-observed — declared required, absent from every payload
  • undeclared-field — appears in traffic but the schema forbids extras

Findings land in x-jerant.drift per operation with a spec-level summary; drifting operations show a on their confidence chip. Disable with driftDetection: false.

13. File uploads / form-data

jerant documents the correct request media type per route:

  • Routes with upload middleware (multer, express-fileupload) are documented as multipart/form-data automatically; the source scan recovers real field names from your config (upload.single('avatar'), upload.fields([{ name: 'id_front' }])).
  • File fields seen at runtime (req.file / req.files) appear as binary inputs alongside schema fields.
  • Explicit control when you want it:
app.post("/avatars",
  upload.single("avatar"),
  doc({ contentType: "multipart/form-data", files: ["avatar"] }),
  handler
);

14. Data safety & redaction

jerant never stores authorization headers, cookies, or session identifiers (headers are not captured at all), and automatically redacts sensitive fields from stored payloads and examples — password, token, secret, apiKey, credit-card fields, and anything that looks like a credential (JWTs, Bearer … values):

{ "password": "***REDACTED***" }

Add your own with redactKeys: ["internal_code"]. With the default examples: "placeholder", stored payloads never surface in the docs at all.

15. Storage & persistence

Learned schemas live in memory by default. To survive restarts:

jerant(app, { storage: { mode: "file", path: ".jerant/data.json" } });

Multiple instances behind a load balancer should share a store instead — anything with load()/save() works (both may be async):

jerant(app, {
  storage: {
    mode: "custom",
    store: {
      load: async () => JSON.parse(await redis.get("jerant") || "null"),
      save: async (data) => redis.set("jerant", JSON.stringify(data)),
    },
  },
});

Writes are debounced and atomic (file mode), with a flush on process exit.

16. Fastify, Koa and Hono

The core is framework-agnostic; adapters ship in the box and provide the same docs, runtime learning, and UI:

// Fastify — route schemas become first-class docs (call before routes)
const { jerantFastify } = require("jerant");
jerantFastify(fastify);

// Koa — pass your @koa/router instance(s)
const { jerantKoa } = require("jerant");
jerantKoa(app, { routers: [router] });

// Hono — call before registering routes
const { jerantHono } = require("jerant");
jerantHono(app);

17. Full configuration reference

Everything is optional:

jerant(app, {
  // Identity (defaults come from your package.json)
  title: "My API",
  version: "2.0.0",
  description: "…",
  servers: [{ url: "https://api.example.com" }],

  // Paths
  docsPath: "/docs",
  openapiPath: "/openapi.json",

  // UI
  ui: "swagger",                   // "redoc" | "scalar" | false | {html} | (ctx) => html
  assets: "cdn",                   // "local" = self-hosted swagger-ui-dist
  cdn: "https://unpkg.com/swagger-ui-dist@5",

  // Documentation behavior
  security: "bearer",              // auth modeling + Authorize button (§5)
  validationError: { status: 422 },// validate() failure shape (§6)
  envelope: { success: "boolean", data: "$data" },   // §7
  defaultResponses: { 500: { description: "Internal error" } },
  tags: [{ name: "users", description: "User management" }],
  examples: "placeholder",         // "captured" | false (§8)
  teachMode: true,                 // open low-confidence ops for learning (§2)
  driftDetection: true,            // §12

  // Learning engine
  runtime: {
    enabled: true,                 // runtime traffic analysis
    sampleRate: 1,                 // sample a fraction of requests
    maxExamples: 3,                // stored examples per route/status
    maxObservations: 1000,         // merge cap per route
    maxBodyBytes: 102400,          // skip larger payloads
  },
  sourceScan: { enabled: true },   // project source analysis
  prisma: { enabled: true },       // schema.prisma tracing (§4)

  // Data safety
  redactKeys: ["internal_id"],

  // Persistence (§15)
  storage: { mode: "memory" },     // "file" | "custom"
});

Disable entirely with enabled: false or the JERANT_DISABLED=1 environment variable.

18. Programmatic API

const docs = jerant(app);
docs.getSpec();   // current OpenAPI document (cached)
docs.refresh();   // force a rebuild
docs.flush();     // persist learned schemas now
docs.close();     // stop persistence timers

19. Production notes

  • Overhead: the middleware only wraps res.json/res.send (restored after each response) and defers all analysis to setImmediate after the response is sent — nothing blocks the request path. Use runtime.sampleRate to sample under heavy load.
  • Call order: call jerant(app) right after express() (before your body parser and routes) so the analyzer sees every response. Route registration order doesn't matter — discovery is lazy.
  • CSP: the /docs page loads UI assets from a CDN by default and uses an inline script. Under a strict Content-Security-Policy use assets: "local" or allow the cdn origin.
  • CI: npx jerant export + npx jerant diff give you spec artifacts and a breaking-change gate without booting a server in tests.

20. Architecture

Express / Fastify / Koa / Hono App
    │
Route Scanner ──► discovers every route & nested router
    │
    ├── Validation Adapters (Zod / Joi / Yup / AJV / express-validator / class-validator)
    ├── Prisma Schema Tracing (schema.prisma → typed body fields)
    └── Runtime Analyzer ──► Schema Inference Engine (+ redaction, confidence, drift)
    │
OpenAPI 3.1 Generator (security, envelopes, tags, default responses, teach mode)
    │
    ├── /openapi.json          (also: npx jerant export / diff)
    └── /docs (dark UI: Swagger / Redoc / Scalar / your own)

Schema adapters and framework adapters both operate on plain instances — nothing in jerant imports a web framework.

Examples

node examples/zod-app.js      # modern app with Zod validation
node examples/legacy-app.js   # legacy app, docs learned from traffic

Roadmap

  • Done: Express/Fastify/Koa/Hono · all major validation libraries · runtime learning + teach mode · security modeling · envelopes & default responses · drift detection · Prisma tracing · export/diff CLI · pluggable storage · dark UI with Swagger/Redoc/Scalar/custom
  • Next: NestJS adapter, TypeScript metadata, SDK generation

License

MIT