@theneo/openapi-scanner
v0.1.0
Published
Scan TypeScript backend code and generate an OpenAPI spec automatically. Zero annotations, zero config.
Downloads
138
Readme
@theneo/openapi-scanner
Scan a TypeScript backend and produce an OpenAPI 3.1 spec. Zero annotations. Zero config.
Point it at your project root. It reads your existing Express routes, resolves TypeScript types via the compiler, and emits a complete OpenAPI document with paths, parameters, request bodies, response schemas, and status codes.
Nothing to add to your source. No JSDoc comments. No decorators. If your code compiles, the scanner can read it.
Install
npm install --save-dev @theneo/openapi-scannerUse (CLI)
npx theneo-scan build --out openapi.jsonScans src/**/*.ts from the project root, writes openapi.json. Watch mode for local dev:
npx theneo-scan dev --out openapi.jsonRescans on save with 150ms debounce.
Use (programmatic)
import { scan } from "@theneo/openapi-scanner";
const doc = await scan({
rootDir: process.cwd(),
entries: ["src/**/*.ts"],
info: { title: "My API", version: "1.0.0" },
});
console.log(doc.paths);Use (with @theneo/express)
Zero-boilerplate integration — one flag enables auto-scanning at Express boot, no CLI step:
import express from "express";
import { apiReference } from "@theneo/express";
const app = express();
// … your routes …
app.use("/api/docs", apiReference({
title: "My API",
scan: true, // scans process.cwd(), serves spec at /api/docs/openapi.json
}));
app.listen(3000);Configuration
Optional theneo.config.{ts,js,mjs,cjs,json} in your project root. CLI flags override config values, config values override defaults.
// theneo.config.ts
import type { ScannerConfig } from "@theneo/openapi-scanner";
export default {
entries: ["src/routes/**/*.ts"],
info: {
title: "Theneo Editor API",
version: "1.1.0",
description: "Programmatic access to companies, members, and editor content.",
},
servers: [
{ url: "https://api.theneo.io", description: "Production" },
{ url: "https://sandbox.api.theneo.io", description: "Sandbox" },
],
overrides: (spec) => {
// Add security schemes or anything else the scanner doesn't infer.
spec.components = {
...spec.components,
securitySchemes: {
bearerAuth: { type: "http", scheme: "bearer" },
},
};
return spec;
},
} satisfies ScannerConfig;What it can infer today
See SUPPORTED-PATTERNS.md for the full matrix. Summary:
| Feature | Support |
|---|---|
| app.get/post/put/patch/delete/options/head/router.get/… | ✅ |
| Nested routers (router.use('/prefix', childRouter)) | ✅ — prefixes chain across arbitrary depth |
| Aliased imports and cross-file router references | ✅ — resolves via TypeScript symbols |
| Path templates (:id → {id}) + reads from req.params.<name> | ✅ |
| Query params from req.query.<name> + destructuring | ✅ |
| Request body from Request<{}, TRes, TBody> generic | ✅ |
| Request body fallback (walk req.body.field reads) | ✅ |
| Response body from res.json(x) / res.send(x) + TS type of x | ✅ |
| Status codes from res.status(N).json(...) chains | ✅ |
| String constant paths (const P = "/foo"; app.get(P, …)) | ✅ |
| Date / URL / Buffer / Map — mapped to sensible schemas | ✅ |
| Recursive object types (cycle-guarded to depth 6) | ✅ |
| Fastify | ⏳ v0.2 |
| NestJS decorators | ⏳ v0.2 |
| Zod schema recognition | ⏳ v0.2 |
| $ref extraction for reused named types | ⏳ v0.2 |
| Promise<T> unwrapping in async handlers | ⏳ v0.2 |
How it works
- Loads your
tsconfig.jsonviats-morph— same compiler front-end astsc. - Two-pass AST walk over your source:
- Pass 1 catalogs every
X.use('/prefix', Y)call as a mount by target symbol. - Pass 2 catalogs every
X.<method>('/path', handler)as a route registration, then joins in prefixes by walking the mount graph.
- Pass 1 catalogs every
- For each registered route:
- Path params from URL template +
req.paramsreads. - Query/header params from
req.query/req.headersreads. - Request body from the handler's typed
reqparameter (falls back to field enumeration). - Response schemas from
res.json()/res.send()/res.end()calls; status codes from.status(N)chain.
- Path params from URL template +
- Emits OpenAPI 3.1.
No source-code annotations required at any step. The scanner treats your TypeScript types as the source of truth.
Version compatibility
- Node 20.13+
- TypeScript 5.0+
- Express 4.x, 5.x
- OpenAPI 3.1 output
License
MIT
