npm package: @structrix/structrix
Description: A robust, scalable base microservice framework for Node.js
Latest version: 1.0.0

# Structrix

A robust, scalable microservice framework for Node.js built on top of Express. Structrix provides a structured, opinionated foundation for building production-ready microservices with built-in support for authentication, caching, rate limiting, metrics, logging, retries, and database integrations.

---

## Table of Contents

- [Features](#features)
- [Installation](#installation)
- [Quick Start](#quick-start)
- [Architecture](#architecture)
- [Core Concepts](#core-concepts)
  - [Structrix (App)](#structrix-app)
  - [BaseService](#baseservice)
  - [BaseController](#basecontroller)
  - [BaseRoutes](#baseroutes)
  - [ServiceFactory](#servicefactory)
- [Configuration](#configuration)
- [Middleware](#middleware)
  - [Authentication](#authentication)
  - [Caching](#caching)
  - [Rate Limiting](#rate-limiting)
  - [Logging](#logging)
  - [Validation](#validation)
  - [Error Handling](#error-handling)
- [Utilities](#utilities)
  - [Logger](#logger)
  - [MetricsCollector](#metricscollector)
  - [RateLimiter](#ratelimiter)
  - [RetryHandler](#retryhandler)
  - [CacheManager](#cachemanager)
  - [Validator](#validator)
- [Database](#database)
  - [Providers](#providers)
  - [Repositories](#repositories)
- [Environment Variables](#environment-variables)
- [Docker](#docker)
- [API Endpoints](#api-endpoints)

---

## Features

- 🏗️ **Modular architecture** — register independent modules (service + controller + routes) at runtime
- 🔐 **Authentication** — JWT, API key, HTTP Basic, and OAuth2 strategies out of the box
- 🗄️ **Caching** — in-memory cache with TTL, tag-based invalidation, and stale-while-revalidate
- ⚡ **Rate limiting** — token-bucket algorithm, per-key limits, and optional Redis backend
- 📊 **Metrics** — Prometheus-compatible metrics via `prom-client` with a built-in `/metrics` endpoint
- 📝 **Structured logging** — Winston-powered logging with daily log rotation and JSON/text formats
- 🔁 **Retry handling** — exponential backoff with configurable retryable error types
- 🗃️ **Database abstraction** — pluggable providers for PostgreSQL, MySQL, and MongoDB
- 🛡️ **Security** — Helmet headers, CORS, compression, and request-ID tracing built in
- 🐳 **Docker ready** — includes `Dockerfile` and `docker-compose.yml`
- 💪 **TypeScript first** — fully typed with strict interfaces throughout

---

## Installation

```bash
npm install structrix
```

Or clone and build from source:

```bash
git clone https://github.com/structrix/structrix.git
cd structrix
npm install
npm run build
```

---

## Quick Start

### 1. Create a Service

Extend `BaseService` and implement the two abstract lifecycle hooks:

```ts
import { BaseService, ServiceOptions } from "structrix";

export class UserService extends BaseService {
  constructor(options: ServiceOptions) {
    super(options);
  }

  protected async onInitialize(): Promise<void> {
    this.logger.info("UserService initialized");
  }

  protected async onShutdown(): Promise<void> {
    this.logger.info("UserService shutting down");
  }

  async getUsers() {
    return this.withCache("users:all", async () => {
      // fetch from DB ...
      return [{ id: "1", name: "Alice" }];
    });
  }
}
```

### 2. Create a Controller

Extend `BaseController`, set the route prefix, and implement `initializeRoutes`:

```ts
import { BaseController } from "structrix";
import { Request, Response } from "express";
import { UserService } from "./user.service";

export class UserController extends BaseController {
  constructor(service: UserService) {
    super("/users", service);
  }

  public initializeRoutes(): void {
    this.router.get("/", this.getUsers.bind(this));
  }

  private async getUsers(req: Request, res: Response): Promise<void> {
    await this.withErrorHandler(req, res, async () => {
      const users = await (this.service as UserService).getUsers();
      this.sendSuccess(res, users);
    });
  }
}
```

### 3. Create Routes

Extend `BaseRoutes` and use `createRoute` to declaratively define your routes with optional auth, caching, and rate limiting:

```ts
import { BaseRoutes } from "structrix";
import { UserController } from "./user.controller";

export class UserRoutes extends BaseRoutes {
  constructor(controller: UserController) {
    super(controller);
  }

  protected configureRoutes(): void {
    this.createRoute({
      path: "/",
      method: "get",
      handler: "getUsers",
      auth: true,
      cache: { ttl: 60 },
      rateLimit: true
    });
  }
}
```

### 4. Boot the Application

```ts
import Structrix from "structrix";
import { UserService } from "./user.service";
import { UserController } from "./user.controller";
import { UserRoutes } from "./user.routes";

const app = new Structrix({
  name: "my-service",
  version: "1.0.0",
  port: 3000
});

app.registerModule({
  name: "users",
  service: UserService,
  controller: UserController,
  routes: UserRoutes,
  config: {
    cache: { enabled: true, ttl: 300 }
  }
});

app.start();
```

---

## Architecture

```
src/
├── index.ts                   # Structrix app entry point & re-exports
├── types/
│   └── global.types.ts        # Shared interfaces (ServiceConfig, HealthStatus, etc.)
├── core/
│   ├── base.service.ts        # Abstract BaseService
│   ├── base.controller.ts     # Abstract BaseController
│   ├── base.routes.ts         # Abstract BaseRoutes
│   ├── base.types.ts          # Core interfaces (IService, IController, etc.)
│   ├── base.repositories.ts   # Abstract BaseRepository
│   ├── database.providers.ts  # BaseDatabaseProvider & QueryOptions
│   └── service.factory.ts     # ServiceFactory singleton
├── config/
│   ├── app.config.ts          # AppConfiguration & extended config interfaces
│   └── database.config.ts     # Postgres, MongoDB, MySQL providers & repositories
├── middleware/
│   ├── auth.middleware.ts     # JWT / API key / Basic / OAuth2 auth
│   ├── cache.middleware.ts    # Response caching & tag invalidation
│   ├── error.middleware.ts    # Global error & 404 handlers
│   ├── logging.middleware.ts  # Request/response logging
│   └── validation.middleware.ts # Joi schema validation for body/query/params
└── utils/
    ├── cacheManager.ts        # In-memory CacheManager (node-cache)
    ├── logger.ts              # Winston logger wrapper
    ├── metrics.ts             # Prometheus MetricsCollector
    ├── rateLimiter.ts         # Token-bucket RateLimiter
    ├── retryHandler.ts        # Exponential-backoff RetryHandler
    └── validator.ts           # Joi-based Validator utility
```

---

## Core Concepts

### Structrix (App)

The top-level class that bootstraps Express, registers global middleware, and manages modules.

```ts
const app = new Structrix(config?: Partial<ServiceConfig>);

await app.registerModule(moduleDef);  // register a module
await app.start();                    // start listening
app.getApp();                         // access the Express instance
app.getConfig();                      // access resolved config
```

Built-in endpoints registered automatically:

| Endpoint       | Description                              |
| -------------- | ---------------------------------------- |
| `GET /health`  | Health status of all registered services |
| `GET /metrics` | Prometheus metrics (when enabled)        |

---

### BaseService

Abstract base class for all services. Provides lifecycle management, logging, metrics, caching, rate limiting, and retry logic.

```ts
abstract class BaseService extends EventEmitter implements IService {
  // Lifecycle
  async initialize(): Promise<void>;
  async shutdown(): Promise<void>;
  async healthCheck(): Promise<boolean>;
  async getHealthStatus(): Promise<HealthStatus>;

  // Helpers
  async withCache<T>(
    key: string,
    fn: () => Promise<T>,
    ttl?: number
  ): Promise<T>;
  async withRetry<T>(fn: () => Promise<T>, context?: string): Promise<T>;
  async withRateLimit<T>(fn: () => Promise<T>, key?: string): Promise<T>;

  // Abstract — implement in your subclass
  protected abstract onInitialize(): Promise<void>;
  protected abstract onShutdown(): Promise<void>;
}
```

**Properties automatically initialized from config:**

| Property       | Description                           |
| -------------- | ------------------------------------- |
| `logger`       | Winston-based Logger instance         |
| `metrics`      | Prometheus MetricsCollector           |
| `cache`        | In-memory CacheManager (if enabled)   |
| `rateLimiter`  | Token-bucket RateLimiter (if enabled) |
| `retryHandler` | Exponential-backoff RetryHandler      |

---

### BaseController

Abstract base class for controllers. Wires up middleware, response helpers, and pagination utilities.

```ts
abstract class BaseController implements IController {
  path: string;
  router: Router;
  service: BaseService;

  // Response helpers
  protected sendSuccess<T>(
    res,
    data?,
    message?,
    pagination?,
    statusCode?
  ): Response;
  protected sendError(res, error, statusCode?, details?): Response;
  protected handleError(res, error): Response;

  // Pagination helpers
  protected getPagination(req): { page: number; limit: number };
  protected getPaginationInfo(total, page, limit): PaginationInfo;

  // Error-catching wrapper
  protected async withErrorHandler(
    req,
    res,
    handler: () => Promise<void>
  ): Promise<void>;
}
```

---

### BaseRoutes

Abstract base class for route configuration. Supports declarative route definitions with built-in auth, cache, and rate-limit middleware injection.

```ts
abstract class BaseRoutes {
  protected abstract configureRoutes(): void;

  protected createRoute(config: RouteConfig): void;
  protected groupRoutes(prefix: string, routes: RouteConfig[]): void;
  public getRouter(): Router;
}

interface RouteConfig {
  path: string;
  method: "get" | "post" | "put" | "patch" | "delete";
  handler: string; // method name on your controller
  middleware?: any[];
  auth?: boolean;
  rateLimit?: boolean;
  cache?: boolean | { ttl: number };
}
```

---

### ServiceFactory

Singleton that manages the lifecycle and registry of all modules.

```ts
const factory = ServiceFactory.getInstance();

await factory.createModule(moduleDef, globalConfig);
factory.getService<T>("users"); // retrieve a service by name
factory.getAllRoutes(); // all registered BaseRoutes
await factory.shutdownAll(); // graceful shutdown
await factory.getHealthStatuses(); // health of all services
```

---

## Configuration

`ServiceConfig` is the core configuration interface. All fields except `name`, `version`, `environment`, and `port` are optional.

```ts
interface ServiceConfig {
  name: string;
  version: string;
  environment: "development" | "production" | "test";
  port: number;
  host?: string;
  timeout?: number;
  maxRetries?: number;
  rateLimit?: {
    enabled: boolean;
    requestsPerMinute: number;
    maxConcurrent?: number;
  };
  cache?: {
    enabled: boolean;
    ttl: number; // seconds
    checkPeriod?: number; // seconds
  };
  logging?: {
    level: "error" | "warn" | "info" | "debug";
    format: "json" | "text";
    output: "console" | "file" | "both";
  };
  metrics?: {
    enabled: boolean;
    collectDefaultMetrics?: boolean;
    prefix?: string;
  };
  auth?: {
    enabled: boolean;
    type: "jwt" | "apikey" | "basic" | "none";
    jwtSecret?: string;
    tokenExpiry?: string;
  };
}
```

---

## Middleware

### Authentication

`AuthMiddleware` supports four strategies. Configure via `AuthConfig`:

| Strategy | Description                                               |
| -------- | --------------------------------------------------------- |
| `jwt`    | Bearer token in `Authorization` header, signed with HS256 |
| `apikey` | Key in a configurable header (default: `x-api-key`)       |
| `basic`  | HTTP Basic auth with a custom validator function          |
| `oauth2` | Bearer token validated against an external token endpoint |

```ts
// Protect a route — roles and permissions are optional
router.get(
  "/admin",
  authMiddleware.authenticate({ roles: ["admin"] }),
  handler
);

// Generate a JWT token
const token = authMiddleware.generateToken(user);

// Hash & verify passwords (bcrypt)
const hash = await authMiddleware.hashPassword("secret");
const valid = await authMiddleware.verifyPassword("secret", hash);
```

---

### Caching

`CacheMiddleware` wraps responses in an in-memory cache. Cache keys are derived from method, path, query parameters, and optionally user ID and headers.

```ts
// Cache responses for 60 seconds
router.get("/items", cacheMiddleware.cache({ ttl: 60 }), handler);

// Invalidate cache entries matching a pattern after a write
router.post("/items", cacheMiddleware.invalidateCache("/items*"), handler);

// Serve stale content while revalidating in the background
router.get(
  "/feed",
  cacheMiddleware.staleWhileRevalidate({ ttl: 30, staleTtl: 120 }),
  handler
);

// Tag-based invalidation
router.get("/posts", cacheMiddleware.cacheWithTags(["posts"]), handler);
router.delete(
  "/posts/:id",
  cacheMiddleware.invalidateByTags(["posts"]),
  handler
);
```

Cache headers added to responses:

| Header          | Value                     |
| --------------- | ------------------------- |
| `X-Cache`       | `HIT`, `MISS`, or `STALE` |
| `X-Cache-Key`   | MD5 hash of the cache key |
| `Cache-Control` | `public, max-age=<ttl>`   |

---

### Rate Limiting

Token-bucket rate limiting, configurable per key (IP by default). Supports a Redis backend for distributed environments.

```ts
// Express middleware — limits by IP
router.use(rateLimiter.middleware());

// Custom key (e.g. authenticated user ID)
router.use(rateLimiter.middleware((req) => req.user?.id ?? req.ip));

// Programmatic acquire (throws if limit exceeded)
await rateLimiter.acquire("user:123");

// Wait for a token (up to maxWaitMs)
await rateLimiter.acquireWithWait("user:123", 5000);

// Redis-backed distributed store
const store = RateLimiter.createRedisStore(redisClient);
const limiter = new RateLimiter({ requestsPerMinute: 100, store });
```

Response headers:

| Header                  | Description                   |
| ----------------------- | ----------------------------- |
| `X-RateLimit-Limit`     | Total tokens per window       |
| `X-RateLimit-Remaining` | Remaining tokens              |
| `X-RateLimit-Reset`     | Unix timestamp of next refill |

---

### Logging

`LoggingMiddleware` logs every request and response with timing, request ID, and optional body capture.

Configured via `ServiceConfig.logging`:

| Option   | Values                           | Default   |
| -------- | -------------------------------- | --------- |
| `level`  | `error`, `warn`, `info`, `debug` | `info`    |
| `format` | `json`, `text`                   | `text`    |
| `output` | `console`, `file`, `both`        | `console` |

Log files rotate daily, kept for 14 days at up to 20 MB per file (in `logs/`).

---

### Validation

`ValidationMiddleware` validates request data against Joi schemas and returns structured `400` errors.

```ts
import Joi from "joi";

const schema = Joi.object({
  name: Joi.string().required(),
  email: Joi.string().email().required()
});

router.post("/users", validationMiddleware.validateBody(schema), handler);

// Also available:
validationMiddleware.validateQuery(schema);
validationMiddleware.validateParams(schema);
```

---

### Error Handling

`ErrorMiddleware` provides a global error handler and a 404 catcher. Register it last on the Express app.

All unhandled errors are returned as:

```json
{
  "success": false,
  "error": "Error message",
  "metadata": {
    "timestamp": "2026-01-01T00:00:00.000Z",
    "requestId": "uuid"
  }
}
```

---

## Utilities

### Logger

Winston-based structured logger with console and/or rotating file transports.

```ts
const logger = new Logger({ level: "info", format: "json", output: "both" });

logger.info("User created", { userId: "123" });
logger.warn("Slow query detected", { duration: 450 });
logger.error("Database connection failed", { error });
logger.debug("Cache miss", { key: "users:all" });
```

---

### MetricsCollector

Prometheus-compatible metrics using `prom-client`. Registers a set of built-in HTTP, cache, database, queue, and error metrics automatically.

```ts
// Increment a counter
metrics.increment("http_requests_total", {
  method: "GET",
  path: "/users",
  status: "200"
});

// Set a gauge
metrics.setGauge("queue_size", 42, { queue_name: "emails" });

// Observe a histogram
metrics.observe("http_request_duration_seconds", 0.123, {
  method: "GET",
  path: "/users"
});

// Time an async function
const result = await metrics.time(
  "database_query_duration_seconds",
  async () => {
    return db.query("SELECT * FROM users");
  },
  { query_type: "select", collection: "users" }
);

// Get Prometheus text output
const output = await metrics.getMetrics();
```

Built-in metrics (prefixed by `ServiceConfig.metrics.prefix`):

| Metric                             | Type      | Description           |
| ---------------------------------- | --------- | --------------------- |
| `_http_requests_total`             | Counter   | Total HTTP requests   |
| `_http_request_duration_seconds`   | Histogram | Request duration      |
| `_http_requests_active`            | Gauge     | Active requests       |
| `_errors_total`                    | Counter   | Total errors          |
| `_cache_operations_total`          | Counter   | Cache hits/misses     |
| `_database_query_duration_seconds` | Histogram | DB query duration     |
| `_queue_size`                      | Gauge     | Current queue depth   |
| `_queue_processed_total`           | Counter   | Processed queue items |

---

### RateLimiter

Token-bucket rate limiter with in-memory or Redis-backed store.

```ts
const limiter = new RateLimiter({
  requestsPerMinute: 60,
  maxConcurrent: 10
});

const info = await limiter.acquire("user:123");
// info.remaining — tokens left
// info.resetTime — epoch ms when bucket refills

await limiter.reset("user:123"); // reset a specific key
await limiter.resetAll(); // reset all (memory store only)
```

---

### RetryHandler

Exponential backoff with configurable max delay and retryable error types.

```ts
const retryHandler = new RetryHandler({
  maxRetries: 3,
  backoffFactor: 2,
  initialDelay: 500, // ms
  maxDelay: 10000 // ms
});

const result = await retryHandler.execute(
  () => externalApi.fetchData(),
  "fetchData" // context label for logs
);
```

Retry delays: `500ms → 1000ms → 2000ms` (with `backoffFactor: 2, initialDelay: 500`).

Network errors (`ECONNRESET`, `ETIMEDOUT`) are always retried. Additional retryable error classes can be passed via `retryableErrors`.

---

### CacheManager

Thin wrapper over `node-cache` with an async interface and set membership helpers for tag-based invalidation.

```ts
const cache = new CacheManager({ ttl: 300, checkPeriod: 60 });

await cache.set("users:all", data, 60);
const data = await cache.get<User[]>("users:all");
await cache.del("users:all");

// Set membership (used by CacheMiddleware for tag invalidation)
await cache.sadd("tag:users", "users:all");
const keys = await cache.smembers("tag:users");

await cache.close();
```

---

### Validator

Joi-based validator utility.

```ts
const validator = new Validator();

const schema = Joi.object({ name: Joi.string().required() });
const isValid = await validator.validate({ name: "Alice" }, schema);
const errors = validator.getErrors();
```

---

## Database

### Providers

Three database providers are available. Each extends `BaseDatabaseProvider`.

```ts
import { DatabaseFactory } from './config/database.config';

const provider = DatabaseFactory.createProvider({
  type: 'postgres',
  host: 'localhost',
  port: 5432,
  username: 'user',
  password: 'pass',
  database: 'mydb'
});

await provider.connect();
const conn = provider.getConnection();
const client = conn.getClient();               // raw pg.Pool / mongoose / mysql2 pool

// Transactions
await conn.transaction(async (session) => {
  await session.query('INSERT INTO ...', [...]);
});

await provider.healthCheck();  // true / false
await provider.disconnect();
```

| Type       | Package    | Notes                             |
| ---------- | ---------- | --------------------------------- |
| `postgres` | `pg`       | Connection pool via `pg.Pool`     |
| `mongodb`  | `mongoose` | Replica set & auth source support |
| `mysql`    | `mysql2`   | Connection pool                   |

The `DatabaseManager` singleton manages multiple named providers:

```ts
import { DatabaseManager } from "./config/database.config";

const db = DatabaseManager.getInstance();
await db.initializeDefault();
await db.registerProvider("analytics", analyticsDbConfig);

const primary = db.getProvider();
const analytics = db.getProvider("analytics");
await db.healthCheckAll();
await db.disconnectAll();
```

### Repositories

Extend `BaseRepository<T>` to build type-safe data access layers. `MongoRepository` and `PostgresRepository` implementations are provided.

```ts
// MongoDB
class UserMongoRepository extends MongoRepository<User> {
  constructor(provider: MongoDBProvider) {
    super(provider, "User", userSchema);
  }
}

const repo = new UserMongoRepository(mongoProvider);
const user = await repo.findById("abc123");
const users = await repo.findAll({ sort: { createdAt: -1 }, limit: 20 });
const created = await repo.create({
  name: "Alice",
  email: "alice@example.com"
});
await repo.update("abc123", { name: "Alicia" });
await repo.delete("abc123");
```

`QueryOptions`:

| Option        | Description                             |
| ------------- | --------------------------------------- |
| `limit`       | Max records to return                   |
| `offset`      | Records to skip                         |
| `sort`        | `{ field: 1 \| -1 }` sort order         |
| `select`      | Fields to include                       |
| `populate`    | Relations to populate (MongoDB)         |
| `lean`        | Return plain objects (no model methods) |
| `transaction` | Session/client to use for transaction   |

---

## Environment Variables

| Variable                | Default       | Description                            |
| ----------------------- | ------------- | -------------------------------------- |
| `PORT`                  | `3000`        | HTTP port                              |
| `HOST`                  | `0.0.0.0`     | Bind address                           |
| `NODE_ENV`              | `development` | Environment                            |
| `TIMEOUT`               | `30000`       | Request timeout (ms)                   |
| `MAX_RETRIES`           | `3`           | Default max retries                    |
| `LOG_LEVEL`             | `info`        | `error` \| `warn` \| `info` \| `debug` |
| `LOG_FORMAT`            | `text`        | `json` \| `text`                       |
| `LOG_OUTPUT`            | `console`     | `console` \| `file` \| `both`          |
| `RATE_LIMIT_ENABLED`    | `false`       | Enable rate limiting                   |
| `RATE_LIMIT_REQUESTS`   | `60`          | Requests per minute                    |
| `RATE_LIMIT_CONCURRENT` | `10`          | Max concurrent requests                |
| `CACHE_ENABLED`         | `false`       | Enable response caching                |
| `CACHE_TTL`             | `300`         | Cache TTL in seconds                   |
| `CACHE_CHECK_PERIOD`    | `60`          | Cache cleanup interval (seconds)       |
| `METRICS_ENABLED`       | `false`       | Enable Prometheus metrics              |
| `METRICS_PREFIX`        | `structrix`   | Metric name prefix                     |
| `AUTH_ENABLED`          | `false`       | Enable authentication                  |
| `AUTH_TYPE`             | `jwt`         | `jwt` \| `apikey` \| `basic` \| `none` |
| `JWT_SECRET`            | —             | Secret key for JWT signing             |
| `TOKEN_EXPIRY`          | `24h`         | JWT expiry (`ms` format string)        |
| `ALLOWED_ORIGINS`       | `*`           | Comma-separated CORS origins           |

Copy `.env.example` to `.env` to get started:

```bash
cp .env.example .env
```

---

## Docker

Build and run with Docker Compose (includes the app service):

```bash
# Build image
npm run docker:build

# Start services
npm run docker:up

# Stop services
npm run docker:down
```

Or with plain Docker:

```bash
docker build -t structrix .
docker run -p 3000:3000 --env-file .env structrix
```

---

## API Endpoints

All module routes are mounted under `/api`. The exact sub-paths depend on the `path` you set in your `BaseController` constructor.

| Endpoint   | Method | Description                               |
| ---------- | ------ | ----------------------------------------- |
| `/health`  | GET    | Service health status (all modules)       |
| `/metrics` | GET    | Prometheus metrics (when metrics enabled) |
| `/api/**`  | \*     | Module routes (registered dynamically)    |

---

## License

MIT