Skip to content

TypeScript & Bundling

Plenum’s node runtime loads plugins via require(), so plugins must be CommonJS modules. You can write them in plain JavaScript or compile from TypeScript using esbuild.

my-example/
src/
my-plugin.ts
dist/
my-plugin.js ← compiled output, loaded by the gateway
package.json
tsconfig.json
openapi.yaml
overlay-gateway.yaml
docker-compose.yaml
{
"private": true,
"scripts": {
"build": "esbuild src/my-plugin.ts --bundle --format=cjs --platform=node --target=node22 --outfile=dist/my-plugin.js"
},
"devDependencies": {
"esbuild": "^0.25.0"
}
}
{
"compilerOptions": {
"target": "ES2022",
"module": "commonjs",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"outDir": "dist"
},
"include": ["src"]
}

The key esbuild flags:

FlagPurpose
--bundleBundle all imports into a single file
--format=cjsOutput CommonJS so the gateway can require() it
--platform=nodeTarget Node.js (includes built-in modules)
--target=node22Match the gateway’s Node.js version
Terminal window
npm install
npm run build

When writing TypeScript with import statements, use standard export syntax for plugin functions:

// ✅ Correct — use export function
import someLib from "some-library";
export function init(options: unknown) {
return {};
}
export function handle(input: PluginInput): PluginOutput {
return { status: 200, headers: {}, body: {} };
}

Do not mix import with exports.xxx = assignments — esbuild treats files with import as ES modules, and CJS-style exports assignments will be silently lost in the bundle:

// ❌ Wrong — exports are lost when mixed with import
import someLib from "some-library";
exports.init = function init() { ... }; // not exported in the bundle
exports.handle = function handle() { ... }; // not exported in the bundle

Point the upstream to the compiled output:

x-plenum-upstream:
kind: "plugin"
plugin: "./dist/my-plugin.js"

Always build before starting the gateway:

Terminal window
npm install
npm run build
docker compose up -d

The dist/ directory is volume-mounted into the container alongside the rest of your config.