@oriolrius/node-red-api-gateway
v0.10.0
Published
Node-RED nodes for API gateway functionality
Readme
@oriolrius/node-red-api-gateway
A comprehensive Node-RED node package for building enterprise-grade REST APIs with built-in support for authentication, authorization, validation, caching, rate limiting, and OpenAPI documentation.
Features
- API Server: Fastify-powered HTTP server with automatic OpenAPI spec generation
- Endpoint Definition: Declarative API endpoint configuration with JSON Schema validation
- Authentication: OAuth2/OpenID Connect via Keycloak integration
- Authorization: OPA (Open Policy Agent) policy enforcement at request time — when OPA is active it is the single authority; scope/role rules are expressed in Rego. Static
requiredScopeschecks remain available for non-OPA endpoints. - Validation: Request/response validation with JSON Schema
- Pagination: Offset-based and cursor-based pagination
- Filtering & Sorting: Configurable field filtering and sorting with SQL clause generation
- Rate Limiting: Token bucket algorithm with per-IP, per-user, or custom key strategies
- Caching: Response caching with ETag support and configurable TTL
- Error Handling: RFC 7807 Problem Details format
- Metrics: Prometheus metrics endpoint for monitoring
- Logging: Structured JSON logging with Pino
Installation
From npm
cd ~/.node-red
npm install @oriolrius/node-red-api-gatewayFor Development
cd ~/.node-red
npm install /path/to/node-red-api-gatewayOr using npm link:
# In the node-red-api-gateway directory
npm link
# In your Node-RED user directory
cd ~/.node-red
npm link @oriolrius/node-red-api-gatewayQuick Start
1. Basic API Setup
Drag an apigw-config node onto the canvas and configure:
- API Version:
v1 - Base Path:
/api - OpenAPI Title:
My API
- API Version:
Drag an apigw-server node and configure:
- Port:
3000 - Config: Select your apigw-config node
- Enable Swagger UI:
true
- Port:
Drag an apigw-endpoint node and configure:
- Path:
/hello - Method:
GET - Server: Select your apigw-server node
- Path:
Connect a function node to handle the request:
msg.res.json({ message: "Hello, World!" });
return null;- Deploy and access:
- API:
http://localhost:3000/api/v1/hello - Swagger UI:
http://localhost:3000/docs - OpenAPI Spec:
http://localhost:3000/openapi.json
- API:
2. Import Example Flows
The package includes several example flows demonstrating different features:
- In Node-RED, click the menu (hamburger icon)
- Select Import > Examples > @oriolrius/node-red-api-gateway
- Choose an example:
- Basic CRUD API - Complete CRUD operations for a products API
- OAuth2 Authenticated API - Keycloak authentication with role-based access
- OPA Protected API - Policy-based access control with Open Policy Agent
- Pagination & Filtering - Advanced list operations with pagination and filtering
Nodes
apigw-config
Configuration node for centralized settings shared across apigw-server and apigw-endpoint nodes.
Database Configuration:
- Database type (PostgreSQL, MSSQL, MySQL, etc.)
- Connection pooling settings
- Credentials (stored securely)
OAuth2/Keycloak Configuration:
- Keycloak URL and realm
- Client ID and secret
- JWT validation settings (issuer, audience, clock tolerance)
OPA Configuration:
- OPA server URL (HTTPS) and the gateway's own OPA service token (credential)
- Policy path (supplied by the OPA administrator) and mode (enforce / monitor / off)
- Fail-open toggle (default fail-closed → 503), token-claim projection, audit fields
- Cache TTL (seconds; 0 disables), timeout, retry, deadline, cache size and circuit-breaker settings
- A Test button that probes reachability, whether the policy path is defined, and a sample decision
When OPA is active for an endpoint, the policy decision is the authorization decision — enabling it is a deployment decision that is invisible to API callers (they authenticate exactly as before).
API Settings:
- Version string and base path
- OpenAPI metadata (title, description, contact, license)
Logging:
- Log level (debug, info, warn, error)
- Output format (console, file)
- Header redaction
apigw-server
HTTP server node that hosts your API endpoints.
Configuration:
- Host and port
- Reference to apigw-config node
- OpenAPI specification endpoint (
/openapi.json) - Swagger UI documentation (
/docs) - Prometheus metrics endpoint (
/metrics), opt-in and served on the API port. Access modes:none(public),bearer(static token),basic(username/password) oroauth2(Keycloak-validated token with optional scopes), plus an optional IP/CIDR allowlist - Request body limit (
bodyLimit, default 1 MiB) and opt-in raw/binary bodies (binaryBodyEnabled,binaryContentTypes, defaultimage/*,application/octet-stream): matching uploads reach the flow as aBufferinmsg.req.body
Metrics security: endpoint auth (Required Scopes, OPA) does not cover
/metrics. With metricsAuth: none anyone who can reach the port can read
process and traffic metrics, so pick an auth mode and/or Allowed IPs on
exposed servers. Prometheus scrape examples:
- job_name: api-gateway
authorization: { credentials: <metrics token> } # metricsAuth: bearer
# basic_auth: { username: prometheus, password: <password> } # metricsAuth: basic
static_configs: [{ targets: ["gateway:3000"] }]Features:
- Automatic route registration from apigw-endpoint nodes
- Route conflict detection
- Graceful shutdown
apigw-endpoint
Individual API endpoint definition node.
Core Settings:
- HTTP method (GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS)
- Path with parameters (e.g.,
/users/:id) - Success status code
- Response content type
Validation:
- Body schema (JSON Schema)
- Query parameter schema
- Path parameter schema
- Response schemas (for OpenAPI documentation)
Authorization:
- Required scopes/roles
- Scope operator (AND/OR)
CRUD Operations:
- Operation type (list, get, create, update, delete)
- Table name and primary key
- Auto-generate SQL templates
Pagination:
- Style: offset-based or cursor-based
- Default and maximum page size
Filtering & Sorting:
- Configurable filterable fields
- Configurable sortable fields
- Default sort field and direction
Transformation:
- Request transformation (JSONata expression)
- Response transformation (JSONata expression)
- Field mappings
Rate Limiting:
- Requests per time window
- Key type (IP, user, API key, custom)
Caching:
- TTL (time to live)
- Cache key strategy
- Vary headers
- ETag support
Error Handling:
- Error format (RFC 7807, generic, custom)
- Stack trace inclusion (dev mode)
- Custom error code mappings
- Auto-CRUD database errors never expose the raw driver message by default:
PK/unique violations →
409, NOT NULL / conversion / truncation / constraint violations →400(FK on delete →409), connection loss →503, anything else →500, each with a genericdetailand arequestId.exposeDbErrors(dev mode) returns the raw message; the RFC 7807typebase URI is set withproblemTypeBaseUrion apigw-config
Examples
Basic CRUD Endpoint
// In your function node connected to an apigw-endpoint
const products = [
{ id: 1, name: "Widget", price: 29.99 },
{ id: 2, name: "Gadget", price: 49.99 }
];
// List endpoint
msg.res.json({
data: products,
total: products.length
});
return null;Request Validation
Configure the apigw-endpoint with a body schema:
{
"type": "object",
"properties": {
"name": { "type": "string", "minLength": 1 },
"price": { "type": "number", "minimum": 0 }
},
"required": ["name", "price"]
}Invalid requests automatically return 400 errors with validation details.
OAuth2 Protected Endpoint
- Configure apigw-config with Keycloak settings
- Set
requiredScopeson apigw-endpoint (e.g.,admin, user:write) - Access user info in your function:
const auth = msg.req.auth;
if (auth.authenticated) {
console.log('User:', auth.preferredUsername);
console.log('Roles:', auth.roles);
}Pagination
Enable pagination on list endpoints:
// Pagination context is populated by apigw-endpoint
const { page, limit, offset } = msg.pagination || {};
// Query database with pagination
const results = await db.query(
`SELECT * FROM products LIMIT ${limit} OFFSET ${offset}`
);
const total = await db.query('SELECT COUNT(*) FROM products');
// Use endpoint's helper to generate metadata
const paginationMeta = msg.endpoint.generatePaginationMeta(
msg.pagination,
{ total: total[0].count, results: results.length }
);
msg.res.json({
data: results,
pagination: paginationMeta
});Rate Limiting
Enable rate limiting on apigw-endpoint:
- Requests:
100 - Window:
60000(1 minute) - Key Type:
ip(oruserfor per-user limits)
Rate limit info is available in msg.rateLimit:
console.log('Remaining requests:', msg.rateLimit.remaining);Response Caching
Enable caching on apigw-endpoint:
- TTL:
30000(30 seconds) - Key Strategy:
full(includes query string) - Vary Headers:
authorization(for user-specific caching)
The cache context is available in msg.cache:
if (msg.cache.hit) {
console.log('Cache hit! Age:', msg.cache.age);
}Development
Prerequisites
- Node.js >= 20.0.0
- npm >= 8.0.0
Setup
npm installRunning Tests
# Run unit tests
npm test
# Run tests in watch mode
npm run test:watchRunning End-to-End Tests
# Start the Docker stack (Node-RED, Keycloak, OPA, SQL Server)
npm run docker:e2e:up
# Run e2e tests
npm run test:e2e
# Stop the Docker stack
npm run docker:e2e:down
# Self-contained hardening e2e: body limits/uploads, /metrics access modes
# (real JWTs) and auto-CRUD DB errors (starts its own SQL Server container)
npm run test:e2e-hardeningLinting
# Check for linting errors
npm run lint
# Fix linting errors automatically
npm run lint:fixDevelopment Node-RED
# Launch a development Node-RED instance
npm run devScripts
Utility scripts are located in the scripts/ directory. See scripts/README.md for details.
Certificate Management
Download SSL certificates from Nginx Proxy Manager for e2e tests (credentials come
from the environment — e.g. NPM_USER/NPM_PASS):
./scripts/download-apigw-cert.shThis is a thin preset over the generic scripts/refresh-cert.sh, which can fetch any
NPM certificate, write it under configurable filenames, and optionally reload the
service that serves it (Node-RED redeploy, systemctl reload nginx, etc.) only when
the cert changed. It uses npm-cli via uvx.
See scripts/README.md for the full environment contract.
Project Structure
node-red-api-gateway/
├── nodes/ # Node-RED nodes
│ ├── api-config.js/html # Configuration node (apigw-config)
│ ├── api-server.js/html # HTTP server node (apigw-server)
│ ├── api-endpoint.js/html # Endpoint definition node (apigw-endpoint)
│ └── icons/ # Node icons
├── lib/ # Shared utility modules
│ ├── path-utils.js # Path manipulation
│ ├── schema-validator.js # JSON Schema validation
│ ├── rate-limiter.js # Token bucket rate limiting
│ ├── response-cache.js # Response caching
│ ├── error-handler.js # Error handling
│ ├── pagination.js # Pagination helpers
│ ├── filtering-sorting.js # Filter/sort parsing
│ ├── crud-generator.js # SQL generation
│ ├── keycloak-client.js # OAuth2/JWT validation
│ ├── opa-client.js # OPA integration
│ ├── openapi-generator.js # OpenAPI spec generation
│ ├── openapi-parser.js # OpenAPI import
│ ├── logger.js # Pino logging
│ └── metrics-collector.js # Prometheus metrics
├── examples/ # Example flows
├── scripts/ # Utility scripts (see scripts/README.md)
├── tests/ # Test suites
│ ├── unit/ # Unit tests
│ └── e2e/ # End-to-end tests
└── package.jsonAPI Reference
Message Properties
When a request hits an apigw-endpoint, the following properties are available on msg:
| Property | Type | Description |
|----------|------|-------------|
| msg.req | Object | Request object (method, url, headers, body, params, query) |
| msg.req.auth | Object | Authentication info (if OAuth2 enabled) |
| msg.res | Object | Response helpers (json, send, status, set, end) |
| msg.endpoint | Object | Endpoint configuration |
| msg.pagination | Object | Parsed pagination params (if enabled) |
| msg.filtering | Object | Parsed filters and WHERE clause (if enabled) |
| msg.sorting | Object | Parsed sorts and ORDER BY clause (if enabled) |
| msg.crud | Object | CRUD operation context (if configured) |
| msg.cache | Object | Cache status (hit, key, age) |
| msg.rateLimit | Object | Rate limit status (allowed, remaining, limit) |
Response Helpers
// Send JSON response
msg.res.json({ data: "value" });
// Set status code
msg.res.status(201).json({ id: 1 });
// Set headers
msg.res.set('X-Custom-Header', 'value');
// End without body
msg.res.status(204).end();License
MIT
Contributing
Contributions are welcome! Please read our contributing guidelines and submit pull requests to the main repository.
