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

@json-express/docs-swagger

v2.0.0

Published

Interactive Swagger UI and dynamic OpenAPI 3.0 specification for JSON Express

Downloads

78

Readme

@json-express/docs-swagger

Documentation provider plugin for JSONExpress v2. Renders interactive Swagger UI at /docs and serves a generated OpenAPI 3.0 specification at /docs/json. The spec is built from your model schemas — not by parsing URLs — so resource grouping and component shapes are authoritative.


What It Does

This plugin implements the IDocProvider interface. When the JSONExpress Kernel boots, it:

  1. Receives the project's ModelSchema[] via setSchemas(schemas) — the same set handed to the database adapter and API generator.
  2. Receives the kernel's RouteDefinition[] via getManifest(routes, req) and renderDocumentation(routes, path, req).
  3. Combines the two: schemas drive what is documented (resource names, field types, required fields, defaults), routes drive how it's exposed (HTTP method, path, security, custom endpoints).

The Transport layer (e.g., @json-express/transport-express) mounts the two HTTP endpoints described below.


Mounted Endpoints

| Method | Path | Description | |---|---|---| | GET | /docs | Interactive Swagger UI — full HTML shell, loads [email protected] from CDN. | | GET | /docs/json | Raw OpenAPI 3.0 spec — the same JSON the UI consumes, useful for codegen and CI checks. |

The mount path is configurable via jex.docs.path (default /docs).


Schema-Driven OpenAPI

For every ModelSchema registered with the kernel that declares fields:

  • components.schemas.<Collection> is generated from schema.fields. Field-level options become OpenAPI keywords:
    • types.string({ minLength, maxLength }){ type: 'string', minLength, maxLength }
    • types.number({ min, max }){ type: 'number', minimum, maximum }
    • types.date(){ type: 'string', format: 'date-time' }
    • types.relation({ target }){ type: 'string', description: 'FK → <target>' }
    • { required: true } → added to the operation's required array
    • { default } → preserved as default
  • Operations are tagged with the resource name (capitalized collection name). Path-segment matching uses the schema's name as the source of truth — longest-match wins, so albums-archive is never shadowed by albums.
  • Request bodies for POST/PATCH/PUT $ref the component schema (#/components/schemas/<Collection>) by default.
  • Response bodies $ref the component schema for resource routes; fall back to { type: 'object' } when no schema match is found (e.g., bespoke routes under routes/).

If a project has no models/ folder, schemas are inferred from data/*.json and the same generation pipeline applies — every demo gets useful Swagger output for free.

Fieldless models (defineRoutes(...) or defineModel({ ... }) without a fields block) are skipped from components.schemas — there's no entity to describe. Their custom endpoints are still documented via the per-route loop below, with a generic { type: 'object' } request body unless a more specific one is wired in.


Inter-Package Integration

Three sanctioned channels (see context/INTER_PACKAGE_ARCHITECTURE.md) let other plugins enrich the Swagger output without coupling:

1. route.metadata.validation — per-op validators

@json-express/api-rest stamps each generated route with the model's relevant validation block, in the shape the validation middleware understands:

// CRUD routes — keyed by op
route.metadata.validation = { create?: { body }, update?: { body }, list?: { query } };
// Custom endpoints — flat
route.metadata.validation = { body?, query? };

When present, this plugin converts the validator to OpenAPI and uses it in place of the model-derived component schema for that single operation — useful when validation rules are stricter or differently shaped than persisted fields (e.g. a confirmPassword field that's never stored).

Status: the introspection codepath in docs-swagger still reads the legacy route.metadata.schema key. Until that's migrated to the new route.metadata.validation shape above, validators authored in models won't show up as OpenAPI request-body overrides. The component schema (from fields) is still emitted correctly.

2. route.metadata.isProtected — Bearer auth

When @json-express/middleware-auth flags a route as protected, the operation gets security: [{ bearerAuth: [] }] and the spec's components.securitySchemes.bearerAuth is populated with { type: 'http', scheme: 'bearer', bearerFormat: 'JWT' }. Swagger UI's Authorize button then unlocks "Try it out" calls with a token.

3. kernel.context — global state

Not used by this plugin today — but the door is open for, say, a future plugin-versioning to pin the spec's info.version at boot.


Configuration

All options use the jex or JEX namespace:

# Activate this plugin instead of the default docs-light
jex.docs=@json-express/docs-swagger

# Override the mount path (default: /docs)
jex.docs.path=/api-docs

# Override the displayed servers[].url — useful behind a reverse proxy
jex.docs.baseUrl=https://api.example.com/v1

# When api-rest mounts under a prefix, the spec auto-prepends it
jex.api.rest.prefix=/api/v1

baseUrl resolution falls through this chain, highest precedence first:

  1. jex.docs.baseUrl (hardcoded override)
  2. x-forwarded-proto + x-forwarded-host (reverse-proxy aware)
  3. req.protocol + req.headers.host + jex.api.rest.prefix

Installation

npm install @json-express/docs-swagger

Then activate it:

jex.docs=@json-express/docs-swagger

The CLI's auto-discovery will swap out the default docs-light provider on the next boot.


Architecture Note

Kernel.boot()
  ├─ schemas finalized (user models + plugin-contributed)
  │     │
  │     ▼
  │   docProvider.setSchemas(schemas)   ← source of truth handed in once
  │
  ├─ apiGenerator.generate(collections) → routes[]
  │
  └─ on first request to /docs:
       docProvider.renderDocumentation(routes, '/docs', req) → HTML shell
       docProvider.getManifest(routes, req)                   → OpenAPI 3.0 spec
              │
              ├─ generate components.schemas from ModelSchema[] (skipping fieldless models)
              ├─ for each route: tag by matching path against schema names
              ├─ honor route.metadata.validation as request-body override (see status note above)
              └─ honor route.metadata.isProtected for security scheme

The plugin holds no shared state between requests — setSchemas is the only stateful call, and it happens once at boot. Spec generation per request is pure given (routes, req) plus the captured schema set.