apigenie-generatorOpenAPI serversserver express
server express
Generate an Express server stub in TypeScript from an OpenAPI document.
Usage
apigenie-generator server express [...OPTIONS] INPUT_FILE
Description
The input must be an OpenAPI 3.0, 3.1 or 3.2 document; bundle documents referencing other files
with apigenie bundle first. The generated code runs on Node.js 20 or later with Express 5.
The generator writes typed handler interfaces and a router serving them. Your code implements the handlers in files of its own, so regenerating the code never overwrites it:
import express from "express";
import { BASE_PATH, createRouter, HttpError, type PetsHandlers } from "pet-store-server";
const pets: Partial<PetsHandlers> = {
async listPets(request) {
const pets = await db.pets.list({ limit: request.query.limit });
return { status: 200, body: pets, headers: { "X-Total-Count": pets.length } };
},
async showPetById(request) {
const pet = await db.pets.find(request.params.petId);
if (pet === undefined) {
throw new HttpError(404, "No such pet");
}
return { status: 200, body: pet };
},
};
const app = express();
app.use(
BASE_PATH,
createRouter(
{ pets },
{
handleNotFound: true,
security: { api_key: ({ credential }) => users.byApiKey(credential) },
},
),
);
app.listen(8080);
Operations are grouped into handler interfaces by their first tag (PetsHandlers); operations
without tags belong to the default group. A handler is named after the operationId of its
operation, or after its method and path. It receives:
- the request: the decoded and validated path, query, header and cookie parameters (
params,query,headers,cookies, by name) and the body with its declared media type (contentType), all typed from the document - the context: the Express request and response (
req,res), the description of the operation and the principal returned by the security handlers
It returns the response: a status, a body, headers and optionally a content type, typed per
status from the document. A handler which answers on its own through context.res returns
nothing.
For every request, the router:
- checks the security requirements of the operation: any of them, with all the schemes of a
requirement, must be satisfied. A security handler receives the credential (the API key, the
bearer token or the decoded
user:passwordof basic authentication) and the required scopes, and returns the principal, orundefinedto reject the request with 401 Unauthorized. It may throw anHttpError, e.g. 403 Forbidden for missing scopes. Schemes without handlers reject every request. - decodes the parameters in all OpenAPI styles (
simple,label,matrix,form,spaceDelimited,pipeDelimitedanddeepObject, exploded or not) and converts them to the types of their schemas; parameters withcontentare parsed as JSON. Absent parameters get their default values. - reads the body: JSON,
application/x-www-form-urlencoded(converted to the types of its schema), text and binary bodies are parsed;multipart/form-databodies are left to middleware such as multer, whosereq.bodyis used. A media type that isn’t declared is answered with 415 Unsupported Media Type, a missing required body with 400. - validates the parameters and the body against their schemas (see
validation) and answers invalid requests with 400 Bad Request. - calls the handler, or answers with 501 Not Implemented when there’s none.
- writes the response, optionally validating it first (
validateResponses).
Errors are answered with problem details
(application/problem+json). Validation errors list the failed checks:
{
"type": "about:blank",
"title": "Bad Request",
"status": 400,
"detail": "The request is invalid",
"errors": [
{ "in": "query", "name": "limit", "path": "", "keyword": "maximum", "message": "must be <= 50" },
{ "in": "body", "path": "/name", "keyword": "required", "message": "must have required property 'name'" }
]
}
Routes are registered so that literal path segments win over parameters (/pets/mine before
/pets/{petId}). Deprecated operations are marked @deprecated, and operations disabled with
the x-apigenie extension (codegen.disabled) are left out. Callbacks and webhooks are not
generated, and operation and path servers are ignored: every route is served under BASE_PATH.
The generated package contains:
src/types.ts: the types of the component schemas, and the request and response types of every operationsrc/handlers.ts: the handler interfaces,Handlers,SecurityHandlersandcreateRouter(handlers, options)src/metadata.ts: the operations (OPERATIONS) and security schemes described for the runtime, and the port (DEFAULT_PORT) and path (BASE_PATH) of the first serversrc/validation.tsand, depending onvalidation,src/schemas.tsandsrc/ajv_engine.ts, orsrc/validators.tssrc/runtime.ts: the request handling code, which doesn’t depend on the documentsrc/document.ts: the OpenAPI document, withcopy_documentsrc/index.ts,package.json,tsconfig.jsonandREADME.mdsrc/server.ts: an example server with stub handlers, withscaffold
README.md and server.ts are written only if they don’t exist; the other files are
overwritten.
Extensions
| Extension | Effect |
|---|---|
x-apigenie.codegen.name |
name of the type of a component schema |
x-apigenie.codegen.controller-name |
on an operation or its path item: the handler interface of the operation, when grouping lists extension |
x-apigenie.codegen.action-name |
name of the handler of an operation, when action_naming lists extension |
x-apigenie.codegen.disabled |
true on an operation or its path item leaves the operation out, unless skip_disabled is false |
x-defaultMapping |
defaultMapping of a discriminator in OpenAPI 3.0 and 3.1 documents: the schema of the values whose discriminator value isn’t mapped |
The extensions can live in an overlay for this target instead of in the document, e.g.
express.overlay.yaml:
overlay: 1.0.0
info:
title: TypeScript names
version: 1.0.0
actions:
- target: $.components.schemas.Pet
update:
x-apigenie:
codegen:
name: Animal
- target: $.paths['/admin']
update:
x-apigenie:
codegen:
disabled: true
apigenie-generator server express openapi.yaml --overlay express.overlay.yaml -o out
Options
-c, –config
Path to the configuration file to use. See the “Configuration” section for more information.
–overlay OVERLAY
Path to an OpenAPI Overlay (1.0 to 1.2) applied to the input before generating, e.g. one setting the
extensions of the “Extensions” section. Can be repeated: the overlays apply in order, after those
listed under commands.generate.server_express.overlay in the configuration file (with paths
relative to it). Warnings and errors about a value an overlay set point to the overlay.
–strict
Makes an overlay action whose target selects nothing an error (exit status 2, nothing is generated)
instead of a warning (OVERLAY:LINE:COL: action #N (TARGET) selects no nodes.).
-o, –output
Directory where the package is generated.
–package-name
Name of the generated package. Defaults to the title of the API followed by -server.
–package-version
Version of the generated package. Defaults to the version of the API.
Configuration
version: '1.0'
commands:
generate:
server_express:
validation: ajv
validate_responses: true
scaffold: true
| Option | Description |
|---|---|
layout |
package (default): an npm package with sources in src/; sources: the sources only |
import_extension |
Extension of relative imports: .js (default, Node.js) or an empty string (bundlers) |
validation |
ajv (default): JSON Schemas validated with Ajv; generated: generated TypeScript validators, without dependencies; none |
validate_responses |
Generate what’s needed to validate responses, enabled by the validateResponses router option (default: false) |
grouping |
How operations are grouped into handler interfaces: tag (default), extension (x-apigenie codegen.controller-name) or url, or a list of them, in order of preference |
action_naming |
Sources of handler names: id, extension (x-apigenie codegen.action-name) or url, or a list of them (default: [id, url]) |
skip_deprecated |
Leave deprecated operations out (default: false) |
skip_disabled |
Leave out operations disabled with x-apigenie (default: true) |
copy_document |
Include the document in the package, so that the router can serve it (default: false) |
scaffold |
Write an example server, src/server.ts, unless it exists (default: false) |
security |
Check the security requirements of operations (default: true) |
Both validation modes report the same issues with the same messages, and both convert OAS 3.0
schemas (nullable, boolean exclusiveMinimum and exclusiveMaximum) and leave readOnly
properties out of the required properties of requests and writeOnly ones out of responses.
The generated validators don’t check if/then/else, dependentRequired,
dependentSchemas, unevaluatedProperties, unevaluatedItems, contains, propertyNames,
contentSchema and $dynamicRef; the generator warns about them. Formats are checked as JSON
Schema defines them, the generated validators knowing the common ones (date-time, date, time,
duration, email, hostname, ipv4, ipv6, uri, uri-reference, uuid, byte,
int32 and int64).
Router options
| Option | Description |
|---|---|
security |
Security handlers, by scheme name |
validation |
A validation engine replacing the generated one, e.g. noValidation |
validateResponses |
Validate the bodies returned by handlers; invalid ones are answered with 500 |
handleNotFound |
Answer requests to unknown paths with 404 and unknown methods with 405 |
notImplemented |
"501" (default) or "next": pass requests to operations without handlers to the next middleware |
exposeErrors |
Put the messages of unexpected errors into 500 responses |
onError |
Called with unexpected errors, which are logged with console.error otherwise |
bodyLimit |
Maximum size of request bodies, e.g. "1mb" |
serveDocument |
Serve the document as JSON at this path, e.g. "/openapi.json" (needs copy_document) |
caseSensitive, strict |
Options of the Express router |