OpenAPI 3.x Express Middleware
exegesis-express provides Express middleware for handling OpenAPI 3.x API definitions, acting as a wrapper around the core `exegesis` library. It offers full support for OpenAPI 3.x.x features including request body parsing (JSON, form-urlencoded), response validation, authentication, and extensibility via a plugin system and custom format validation. The current stable version is 4.0.0, released in December 2021. While not on a fixed release cadence, it typically updates in conjunction with its parent `exegesis` library, incorporating bug fixes, security patches, and Node.js version support changes. Key differentiators include comprehensive OpenAPI 3.x conformance, flexible middleware placement, and robust error handling for API-driven applications.
Common errors
-
Error: No handler found for operationId 'myOperation' for '/my-path'
cause The `operationId` specified in your OpenAPI document does not correspond to an exported function in the designated controller module, or the `x-exegesis-controller` path is incorrect, or `options.controllers` is misconfigured.fixVerify that `options.controllers` in your middleware setup points to the correct directory. Confirm that the controller file exists and exports a function with the exact `operationId` name (e.g., `export function myOperation(...)`). Check the `x-exegesis-controller` value in your OpenAPI definition matches the controller file name (without extension). -
TypeError: Cannot read properties of undefined (reading 'query')
cause This often occurs within a controller function trying to access `context.params.query` or `context.params.path` when a parameter expected is missing or has an incorrect `in` location (e.g., expecting `query` but parameter is `path`).fixReview your OpenAPI specification to ensure that parameters are correctly defined with their `in` property (e.g., `in: 'query'`, `in: 'path'`) and that your controller code correctly accesses them (e.g., `context.params.query.myParam`). Add `required: true` to OpenAPI parameters if they are mandatory. -
Response validation error: ... must be string (or similar AJV error)
cause The data returned by your controller function does not conform to the `response` schema defined in your OpenAPI document for that operation, or the `onResponseValidationError` option is firing.fixCompare the data structure and types returned by your controller's operation function against the OpenAPI response schema (`paths['/your-path'].get.responses['200'].content['application/json'].schema`). Adjust either your controller's return value or the OpenAPI schema to match. If `onResponseValidationError` is set, its callback will be invoked.
Warnings
- breaking Version 4.0.0 dropped support for Node.js 10. Users on older Node.js versions must remain on v3.x or upgrade their Node.js environment.
- breaking Version 3.0.0 upgraded the underlying `ajv` library to 8.3.0, which may result in changes to validation error messages. This can affect automated tests or client-side error handling that relies on specific error string matching.
- breaking Version 2.0.0 dropped support for Node.js 6. Applications running on Node.js 6 must not upgrade to v2.0.0 or higher.
- gotcha `exegesis-express` should generally be placed high in your Express middleware stack, *before* any other body-parsing middlewares (like `express.json()` or `body-parser`). This allows `exegesis` to handle request body parsing according to the OpenAPI specification.
- gotcha OpenAPI paths intended to be handled by `exegesis-express` must define the `x-exegesis-controller` extension and `operationId` on each operation. Without these, `exegesis` cannot map incoming requests to the correct controller functions.
Install
-
npm install exegesis-express -
yarn add exegesis-express -
pnpm add exegesis-express
Imports
- middleware
const { middleware } = require('exegesis-express');import { middleware } from 'exegesis-express'; - ExegesisOptions
import type { ExegesisOptions } from 'exegesis-express'; - OpenApiDocument
import type { OpenApiDocument } from 'exegesis-express';
Quickstart
import express from 'express';
import { middleware } from 'exegesis-express';
import path from 'path';
const openApiDoc = {
openapi: '3.0.3',
info: {
title: 'Example API',
version: '1.0.0',
},
paths: {
'/greet': {
get: {
'x-exegesis-controller': 'GreetingController',
operationId: 'greetUser',
parameters: [
{
name: 'name',
in: 'query',
schema: { type: 'string', default: 'World' },
required: false,
},
],
responses: {
'200': {
description: 'A greeting message.',
content: {
'application/json': {
schema: { type: 'object', properties: { message: { type: 'string' } } },
},
},
},
},
},
},
},
};
// Create a simple controller file (e.g., controllers/GreetingController.ts)
// export function greetUser(context) {
// const name = context.params.query.name || 'World';
// return { message: `Hello, ${name}!` };
// }
async function createServer() {
const app = express();
const exegesisMiddleware = await middleware(openApiDoc, {
controllers: path.resolve(__dirname, 'controllers'),
allowMissingControllers: false,
onResponseValidationError: (error) => {
console.error('Response validation error:', error);
},
});
// Exegesis-express should be placed before any body parsers if you want
// it to handle request body parsing itself.
app.use(exegesisMiddleware);
// Catch-all for routes not handled by Exegesis (e.g., 404)
app.use((req, res) => {
res.status(404).json({ message: 'Not Found' });
});
// Global error handler
app.use((err, req, res, next) => {
console.error('Unhandled error:', err);
res.status(500).json({ message: `Internal server error: ${err.message}` });
});
app.listen(3000, () => {
console.log('Server listening on http://localhost:3000');
console.log('Try: curl http://localhost:3000/greet?name=Alice');
});
}
createServer().catch(console.error);