OpenAPI Native API Gateway
Route, transform and validate requests, all configured in your OpenAPI schema.
Docker
docker run ghcr.io/thesoftwarebakery/plenum # Standard OpenAPI — Plenum reads this natively
openapi: "3.1.0"
info:
title: Orders API
version: "1.0.0"
x-plenum-upstream:
kind: "HTTP"
address: localhost
port: 3001
x-plenum-interceptor:
- module: "internal:validate-request"
hook: on_request
function: validateRequest
- module: "./interceptors/auth.js"
hook: on_request_headers
function: auth
paths:
/orders:
post:
summary: Create order How Plenum reads your config
Keep your OpenAPI spec clean with overlays and interceptors
Plenum loads your OpenAPI file, applies environment overlays, and wires in Node.js interceptors and plugins, all from the same directory.
openapi.yaml
# Plenum reads x-plenum-* extensions natively
openapi: "3.1.0"
info:
title: Orders API
version: "1.0.0"
x-plenum-upstream:
kind: "HTTP"
address: localhost
port: 3001
x-plenum-interceptor:
- module: "internal:validate-request"
hook: on_request
function: validateRequest
- module: "./interceptors/auth.js"
hook: on_request_headers
function: auth
paths:
/orders:
post:
summary: Create order
requestBody:
required: true production.overlay.yaml
# Applied on top of openapi.yaml in production
overlay: "1.1.0"
info:
title: Production overrides
actions:
- target: "$.x-plenum-upstream"
update:
kind: "HTTP"
address: "${env.UPSTREAM_HOST}"
port: 443
tls: true interceptors/auth.js
// Full Node.js — use any npm package
const { verify } = require('jsonwebtoken');
exports.auth = function auth(request) {
const token = request.headers
['authorization']
?.replace('Bearer ', '');
if (!token) {
return {
action: "respond",
status: 401,
headers: { "content-type": "application/json" },
body: { error: "Missing authorization header" },
};
}
return { action: "continue", ctx: { token } };
}; plugins/postgres.js
// Define a custom upstream type
const { Pool } = require('pg');
let pool;
exports.init = function init(options) {
pool = new Pool({
connectionString: options.connectionString,
});
return {};
};
exports.handle = async function handle(input) {
const { query, params } = input.config;
const result = await pool.query(query, params);
return {
status: 200,
headers: { "content-type": "application/json" },
body: result.rows,
};
};