npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

opticore-api-gateway

v1.0.0

Published

OptiCore API Gateway

Readme

opticore-api-gateway

A flexible, production-ready API Gateway built in TypeScript, powered by the Opticore ecosystem. It provides dynamic routing, load balancing, middleware chaining, circuit breaking, and service registry out of the box.


Table of Contents


Features

  • Dynamic Routing — Define routes with HTTP method, path, and target service(s)
  • Load Balancing — Four strategies: round-robin, weighted, random, least-connections
  • Service Registry — Automatic service health tracking and instance management
  • Circuit Breaker — Per-route circuit breaker with configurable thresholds and reset timeouts
  • Middleware Chain — Global and per-route middleware support (Express-compatible signature)
  • Request Forwarding — Transparent proxying with header rewriting and tracing headers
  • Logging — Built-in LoggingMiddleware with timing and status code
  • Validation — Built-in ValidationMiddleware using opticore-validator
  • opticore-webapp Integration — Drop-in replacement for registerRouter() in any Opticore template project

Request lifecycle:

Incoming request
   │
   ▼
Global middlewares (auth, rate-limit, logging, …)
   │
   ▼
Route matched by method + path prefix
   │
   ▼
Route-level middlewares
   │
   ▼
Load balancer selects a healthy service instance
   │
   ▼
Request forwarded to target with tracing headers
   │
   ▼
Response sent back to client

Installation

npm install opticore-api-gateway

Requires Node.js >= 16 and TypeScript >= 5.


Quick Start — Standalone mode

The gateway creates its own HTTP server and proxies requests to backend services.

import {
    APIGateway,
    AuthMiddleware,
    RateLimitMiddleware,
    LoggingMiddleware
} from 'opticore-api-gateway';

const gateway = new APIGateway({
    port: 3000,
    loadBalancer: 'round-robin',
    enableLogging: true,

    services: [
        {
            name: 'user-service',
            url: 'http://localhost:4001',
            healthCheck: '/health',
            timeout: 5000,
            retries: 3,
        },
        {
            name: 'order-service',
            url: 'http://localhost:4002',
            healthCheck: '/health',
        },
    ],

    globalMiddlewares: [
        new LoggingMiddleware('info'),
        new RateLimitMiddleware(100, 60000),
    ],

    routes: [
        {
            path: '/users',
            method: 'get',
            target: 'http://localhost:4001',
            serviceName: 'user-service',
            timeout: 5000,
        },
        {
            path: '/orders',
            method: 'post',
            target: 'http://localhost:4002',
            serviceName: 'order-service',
            middlewares: [new AuthMiddleware(['my-secret-key'])],
            circuitBreaker: {
                failureThreshold: 5,
                resetTimeout: 30000,
                halfOpenMaxAttempts: 2,
            },
        },
    ],
});

await gateway.start();
// → API Gateway running on port 3000
// → Registered 2 routes

Integration with opticore-webapp

When you install opticore-api-gateway in an Opticore template project (e.g. opticore-api-restfull-template-mysql), the gateway can replace the registerRouter() call and manage all routing, middleware, and proxying.

How it works

gateway.getOpticoreRoutes() returns a TFeatureRoutes[] array that is directly passed to WebServerCore.onStartServer(). Each gateway route is mounted on the Express app with its global middlewares already embedded.

Step-by-step integration

1. Install the package

npm install opticore-api-gateway

2. Replace registerRouter() in src/bootstrap/server/webApp.server.ts

import {
    APIGateway,
    AuthMiddleware,
    RateLimitMiddleware,
    LoggingMiddleware
} from 'opticore-api-gateway';

const gateway = new APIGateway({
    port: 4200,   // informational — Express port is managed by WebServerCore
    services: [
        { name: 'user-service',    url: 'http://user-service:4001' },
        { name: 'product-service', url: 'http://product-service:4002' },
    ],
    routes: [
        {
            path: '/api/users',
            method: 'get',
            target: 'http://user-service:4001',
            serviceName: 'user-service',
        },
        {
            path: '/api/products',
            method: 'get',
            // Multiple targets → load balancing is activated
            target: ['http://product-service-1:4002', 'http://product-service-2:4002'],
            serviceName: 'product-service',
        },
    ],
    globalMiddlewares: [
        new LoggingMiddleware('info'),
        new RateLimitMiddleware(100, 60000),
        new AuthMiddleware(['my-api-key']),
    ],
    loadBalancer: 'round-robin',
    enableLogging: true,
});

// Pass gateway routes instead of registerRouter()
webApp.onStartServer(gateway.getOpticoreRoutes(), dbConnection, dependenciesProvider);

3. (Optional) Apply gateway middlewares globally via Express

If you want gateway middlewares to also run on routes outside the gateway (e.g. static files, error pages), use getExpressMiddleware():

// Before calling onStartServer:
expressApp.use(gateway.getExpressMiddleware());

What getOpticoreRoutes() does internally

  • Creates one Express Router per configured route, mounted at config.path
  • Each router handles the * wildcard — so /api/users/123, /api/users?page=2 etc. are all captured
  • The handler restores req.url to req.originalUrl (full path) before proxying so URL rewriting is correct
  • Global middlewares run before the proxy forward for every matched request

Configuration

IGatewayConfig interface

| Property | Type | Required | Description | |---------------------|--------------------------|----------|-----------------------------------------------| | port | number | ✅ | Port used by start() (standalone mode) | | services | IServiceConfig[] | ✅ | Backend services to register | | routes | IRouteConfig[] | ✅ | Route definitions | | globalMiddlewares | any[] | ❌ | Middlewares applied to every request | | loadBalancer | TLoadBalancingStrategy | ❌ | Default: round-robin | | enableLogging | boolean | ❌ | Logs request duration when true | | enableMetrics | boolean | ❌ | Reserved for future metrics collection |


IRouteConfig interface

| Property | Type | Required | Description | |------------------|-------------------------|----------|---------------------------------------------------------| | path | string | ✅ | URL path prefix (e.g. /api/users) | | method | HttpMethod | ✅ | get, post, put, delete, patch, options… | | target | string \| string[] | ✅ | Target URL(s). Array enables load balancing. | | middlewares | any[] | ❌ | Route-specific middlewares (run after global ones) | | timeout | number | ❌ | Request timeout in ms (default: 30000) | | retries | number | ❌ | Number of retry attempts on failure | | circuitBreaker | ICircuitBreakerConfig | ❌ | Circuit breaker configuration | | serviceName | string | ❌ | Explicit service name override for registry lookup |


IServiceConfig interface

| Property | Type | Required | Description | |---------------|----------|----------|------------------------------------------------| | name | string | ✅ | Unique service identifier | | url | string | ✅ | Base URL of the service | | healthCheck | string | ❌ | Health check endpoint path (e.g. /health) | | weight | number | ❌ | Weight for weighted load balancing strategy | | timeout | number | ❌ | Default request timeout in ms | | retries | number | ❌ | Default retry count |


ICircuitBreakerConfig interface

| Property | Type | Description | |-----------------------|----------|--------------------------------------------------------| | failureThreshold | number | Failures before the circuit opens | | resetTimeout | number | Time in ms before transitioning to half-open | | halfOpenMaxAttempts | number | Max probe requests allowed in half-open state |

Circuit states:

  • closed — Normal operation. Requests are forwarded.
  • open — Service is unhealthy. Requests fail fast (503).
  • half-open — Limited probing to test recovery.

Load Balancing Strategies

| Strategy | Description | |---------------------|---------------------------------------------------------------------| | round-robin | Distributes requests evenly across all healthy instances (default) | | weighted | Routes more traffic to instances with a higher weight value | | random | Selects a healthy instance at random | | least-connections | Selects the instance with the fewest active connections |

const gateway = new APIGateway({
    loadBalancer: 'least-connections',
    // ...
});

Multi-target route (load balancing across instances):

{
    path: '/api',
    method: 'get',
    target: [
        'http://service-1:4000',
        'http://service-2:4000',
        'http://service-3:4000',
    ],
    serviceName: 'my-service',
}

Middleware

All middlewares follow an Express-compatible signature:

type TMiddlewareFunction = (req: any, res: any, next: () => void) => void | Promise<void>;

Global Middlewares

Applied to every matched request before the proxy forward:

const gateway = new APIGateway({
    globalMiddlewares: [
        new LoggingMiddleware('info'),
        new RateLimitMiddleware(100, 60000),
        new AuthMiddleware(['key-abc', 'key-xyz']),
    ],
    // ...
});

Add global middlewares at runtime:

gateway.addMiddleware((req, res, next) => {
    console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`);
    next();
});

Route-Level Middlewares

Applied only to a specific route, after global middlewares:

{
    path: '/admin',
    method: 'get',
    target: 'http://admin-service:5000',
    middlewares: [
        new AuthMiddleware(['admin-key']),
        (req, res, next) => {
            if (!req.headers['x-admin-token']) {
                res.statusCode = 403;
                res.end(JSON.stringify({ error: 'Forbidden' }));
                return;
            }
            next();
        },
    ],
}

Built-in Middlewares

AuthMiddleware

Validates x-api-key header (or ?apiKey= query param):

import { AuthMiddleware } from 'opticore-api-gateway';

new AuthMiddleware(['secret-key-1', 'secret-key-2'])
  • Returns 401 if no key is provided
  • Returns 403 if the key is invalid

RateLimitMiddleware

Sliding-window rate limiting per client IP:

import { RateLimitMiddleware } from 'opticore-api-gateway';

new RateLimitMiddleware(100, 60000)  // 100 requests per 60 seconds

Sets X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset response headers.
Returns 429 when the limit is exceeded.

LoggingMiddleware

Logs method, URL, status code, and duration on response finish:

import { LoggingMiddleware } from 'opticore-api-gateway';

new LoggingMiddleware('info')   // 'info' | 'debug' | 'error'

ValidationMiddleware

Validates request body (or query/params) using opticore-validator:

import { ValidationMiddleware } from 'opticore-api-gateway';

new ValidationMiddleware(mySchema, 'body')  // 'body' | 'query' | 'params' | 'all'

Returns 400 with field-level errors if validation fails.

Creating Custom Middlewares

Extend BaseMiddleware and implement handle():

import { BaseMiddleware, TMiddlewareFunction } from 'opticore-api-gateway';

export class CorsMiddleware extends BaseMiddleware {
    handle(): TMiddlewareFunction {
        return (req, res, next) => {
            res.setHeader('Access-Control-Allow-Origin', '*');
            res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
            next();
        };
    }
}

API Reference

APIGateway

Main gateway class.

Constructor

new APIGateway(config: IGatewayConfig)

Methods

| Method | Description | |----------------------------------------------|--------------------------------------------------------------------------| | start(): Promise<void> | Starts a standalone HTTP server on the configured port | | stop(): Promise<void> | Gracefully shuts down the HTTP server | | addRoute(config: IRouteConfig) | Dynamically registers a new route at runtime | | addMiddleware(fn: TMiddlewareFunction) | Adds a global middleware at runtime | | registerService(service: IServiceConfig) | Registers a new service in the service registry | | getOpticoreRoutes(): TFeatureRoutes[] | Returns routes compatible with WebServerCore.onStartServer() | | getExpressMiddleware() | Returns global middlewares as a single Express middleware function |


GatewayRoute

Encapsulates a single route and its Opticore integration.

Constructor

new GatewayRoute(config: IRouteConfig, handler: TRouteHandler, localLanguage: string)

Methods

| Method | Return Type | Description | |---------------------------------------------------------------|---------------------------|----------------------------------------------| | getConfig() | IRouteConfig | Returns the route configuration | | getStandaloneRoute(strategy?, options?) | Express Router | Returns the standalone Express router | | createMultipleRouterConfig(controller) | IMultipleRouterConfig | Generates an Opticore multi-router config | | createCollectionRouter(controller, routes) | void | Initializes a collection router | | getMultipleRouteDefinition() | IMultipleRouteDefinition| Returns the collection router definition | | GatewayRoute.createOpticoreRouteDefinition(path, handler) | { path, handler } | Static factory for Opticore route entries |


ServiceRegistry

Manages service instances and health state.

| Method | Description | |--------------------------------------------|---------------------------------------------------------| | registerService(service: IServiceConfig) | Registers a new service and starts health check | | getHealthyInstances(serviceName: string) | Returns healthy (circuit-closed) instances | | recordSuccess(serviceName, url) | Resets failure counters; may close circuit | | recordFailure(serviceName, url) | Increments failure count; may open circuit | | deregisterService(serviceName, url) | Removes an instance and clears its health check | | getAllServices() | Returns the full service map |


LoadBalancer

Selects a service instance based on the configured strategy.

const lb = new LoadBalancer('weighted');
const instance = lb.selectInstance('user-service', healthyInstances);
lb.setStrategy('round-robin');

HttpClient

Low-level HTTP/HTTPS client used internally to forward requests.

const client = new HttpClient();
const response = await client.request('http://service:4000/api', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ key: 'value' }),
    timeout: 5000,
});
// response: { status, headers, body }

Convenience methods: client.get(url), client.post(url, body), client.put(url, body), client.patch(url, body), client.delete(url).


Types & Interfaces

| Name | Description | |--------------------------|--------------------------------------------------------------| | TMiddlewareFunction | Express-compatible middleware signature | | TGatewayContext | Opticore context extended with serviceUrl, serviceInstance, gatewayStartTime | | TLoadBalancingStrategy | 'round-robin' \| 'weighted' \| 'random' \| 'least-connections' | | IGatewayConfig | Root gateway configuration | | IRouteConfig | Individual route configuration | | IServiceConfig | Backend service declaration | | IServiceInstance | Runtime service instance with health state | | ICircuitBreakerConfig | Circuit breaker thresholds | | IHttpRequestOptions | Options passed to HttpClient.request() | | IHttpResponse | Response shape returned by HttpClient |


Advanced Usage

Adding a route at runtime

gateway.addRoute({
    path: '/notifications',
    method: 'get',
    target: 'http://notification-service:6000',
    serviceName: 'notification-service',
    timeout: 3000,
    retries: 2,
});

Custom load balancer strategy

Implement ILoadBalancerStrategy to plug in your own logic:

import { ILoadBalancerStrategy } from 'opticore-api-gateway';
import { IServiceInstance } from 'opticore-api-gateway';

export class StickySessionStrategy implements ILoadBalancerStrategy {
    selectInstance(serviceName: string, instances: IServiceInstance[]): IServiceInstance | null {
        // Custom sticky-session logic based on session ID, user ID, etc.
        return instances[0] ?? null;
    }
}

Accessing the gateway context

TGatewayContext extends Opticore's ICustomContext with gateway-specific fields:

type TGatewayContext = ICustomContext & {
    serviceUrl?: string;              // Resolved target URL
    serviceInstance?: IServiceInstance; // Selected backend instance
    gatewayStartTime?: number;        // Request start timestamp (ms)
};

Error Handling

| Scenario | HTTP Status | Response Body | |----------------------------|-------------|--------------------------------------------------| | No healthy instances | 503 | { "error": "Service unavailable" } | | Load balancer returns null | 503 | { "error": "No healthy instances available" } | | Forwarding / proxy error | 502 | { "error": "Bad Gateway", "message": "..." } | | Middleware error | 500 | { "error": "Internal Server Error" } | | Route not found | 404 | { "error": "Route not found" } | | Missing API key | 401 | { "error": "API key required" } | | Invalid API key | 403 | { "error": "Invalid API key" } | | Rate limit exceeded | 429 | { "error": "Too many requests", "retryAfter": N } |


License

MIT