CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-inversify-express-utils

Express utilities and decorators for building web applications with Inversify dependency injection container

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

middleware.mddocs/

Middleware System

Middleware integration system supporting both Express middleware and custom Inversify-based middleware with dependency injection support. The system allows for flexible middleware composition at both controller and method levels.

Capabilities

Base Middleware Class

Abstract base class for creating custom middleware that integrates with Inversify dependency injection.

/**
 * Abstract base class for middleware with dependency injection support
 */
abstract class BaseMiddleware {
  /** HTTP context initialized when middleware is invoked */
  httpContext: HttpContext;

  /**
   * Binds a service to the request-scoped container
   * @param serviceIdentifier - Service identifier to bind
   * @returns Binding syntax for fluent configuration
   */
  protected bind<T>(
    serviceIdentifier: interfaces.ServiceIdentifier<T>
  ): interfaces.BindingToSyntax<T>;

  /**
   * Abstract handler method that must be implemented by concrete middleware
   * @param req - Express request object
   * @param res - Express response object
   * @param next - Express next function
   */
  abstract handler(
    req: Request,
    res: Response,
    next: NextFunction
  ): void | Promise<void>;
}

Middleware Decorator

Decorator for applying middleware to controllers or individual methods.

/**
 * Applies middleware to a controller class or method
 * @param middleware - Middleware functions or service identifiers to apply
 * @returns Decorator function for classes or methods
 */
function withMiddleware(...middleware: Middleware[]): ClassDecorator | MethodDecorator;

Middleware Types

Type definitions for middleware integration.

/**
 * Middleware type supporting both Express handlers and Inversify service identifiers
 */
type Middleware = interfaces.ServiceIdentifier | RequestHandler;

/**
 * Middleware metadata interface for storing middleware configuration
 */
interface MiddlewareMetaData {
  [identifier: string]: Middleware[];
}

Usage Examples:

import { injectable } from "inversify";
import { 
  BaseMiddleware, 
  withMiddleware,
  controller,
  httpGet,
  BaseHttpController 
} from "inversify-express-utils";

// Custom Middleware with Dependency Injection
@injectable()
class LoggingMiddleware extends BaseMiddleware {
  async handler(req: Request, res: Response, next: NextFunction): Promise<void> {
    const startTime = Date.now();
    
    // Access injected services through HTTP context
    const logger = this.httpContext.container.get<Logger>("Logger");
    
    logger.info(`${req.method} ${req.path} - Request started`);
    
    // Continue to next middleware/handler
    next();
    
    // Log completion (this runs after the response)
    res.on("finish", () => {
      const duration = Date.now() - startTime;
      logger.info(`${req.method} ${req.path} - Completed in ${duration}ms`);
    });
  }
}

@injectable()
class AuthenticationMiddleware extends BaseMiddleware {
  async handler(req: Request, res: Response, next: NextFunction): Promise<void> {
    const token = req.headers.authorization;
    
    if (!token) {
      res.status(401).json({ error: "Authentication required" });
      return;
    }

    try {
      // Use services from container
      const authService = this.httpContext.container.get<AuthService>("AuthService");
      const user = await authService.validateToken(token);
      
      // Store user in request for later use
      (req as any).user = user;
      next();
    } catch (error) {
      res.status(401).json({ error: "Invalid token" });
    }
  }
}

@injectable()
class RateLimitMiddleware extends BaseMiddleware {
  async handler(req: Request, res: Response, next: NextFunction): Promise<void> {
    const cacheService = this.httpContext.container.get<CacheService>("CacheService");
    const clientIp = req.ip;
    const key = `rate_limit_${clientIp}`;
    
    const requestCount = await cacheService.increment(key, 60); // 60 second window
    
    if (requestCount > 100) { // 100 requests per minute
      res.status(429).json({ error: "Rate limit exceeded" });
      return;
    }
    
    next();
  }
}

// Express Middleware Functions
function corsMiddleware(req: Request, res: Response, next: NextFunction) {
  res.header("Access-Control-Allow-Origin", "*");
  res.header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
  res.header("Access-Control-Allow-Headers", "Content-Type, Authorization");
  
  if (req.method === "OPTIONS") {
    res.sendStatus(204);
    return;
  }
  
  next();
}

function compressionMiddleware(req: Request, res: Response, next: NextFunction) {
  // Simple compression logic
  const originalSend = res.send;
  res.send = function(body) {
    if (typeof body === "string" && body.length > 1000) {
      res.header("Content-Encoding", "gzip");
      // Compress body (pseudo-code)
      body = compress(body);
    }
    return originalSend.call(this, body);
  };
  next();
}

Controller-Level Middleware

Apply middleware to all methods in a controller.

// Using decorator syntax
@controller("/api/users")
@withMiddleware(LoggingMiddleware, AuthenticationMiddleware)
class UserController extends BaseHttpController {
  // All methods inherit the middleware
  
  @httpGet("/")
  getUsers() {
    return this.ok(["user1", "user2"]);
  }

  @httpPost("/")
  createUser(@requestBody() userData: any) {
    return this.created("/users/123", userData);
  }
}

// Using constructor parameter
@controller("/api/products", LoggingMiddleware, corsMiddleware)
class ProductController extends BaseHttpController {
  @httpGet("/")
  getProducts() {
    return this.ok(["product1", "product2"]);
  }
}

Method-Level Middleware

Apply middleware to specific methods within a controller.

@controller("/api/admin")
class AdminController extends BaseHttpController {
  @httpGet("/users")
  @withMiddleware(AuthenticationMiddleware, RateLimitMiddleware)
  getUsers() {
    return this.ok(["user1", "user2"]);
  }

  @httpPost("/users")
  @withMiddleware(AuthenticationMiddleware, compressionMiddleware)
  createUser(@requestBody() userData: any) {
    return this.created("/users/123", userData);
  }

  // No additional middleware on this method
  @httpGet("/health")
  getHealth() {
    return this.ok({ status: "healthy" });
  }
}

Mixed Middleware Types

Combine Inversify middleware and Express middleware in the same application.

@controller("/api/files")
@withMiddleware(corsMiddleware, LoggingMiddleware) // Express + Inversify middleware
class FileController extends BaseHttpController {
  @httpPost("/upload")
  @withMiddleware(AuthenticationMiddleware, compressionMiddleware)
  uploadFile(@requestBody() fileData: any) {
    return this.created("/files/123", fileData);
  }

  @httpGet("/:id")
  @withMiddleware(RateLimitMiddleware)
  getFile(@requestParam("id") id: string) {
    return this.ok({ id, name: "file.txt" });
  }
}

Middleware Registration

Register custom middleware with the Inversify container.

Usage Examples:

import { Container } from "inversify";

const container = new Container();

// Register middleware classes
container.bind<LoggingMiddleware>("LoggingMiddleware").to(LoggingMiddleware);
container.bind<AuthenticationMiddleware>("AuthenticationMiddleware").to(AuthenticationMiddleware);
container.bind<RateLimitMiddleware>("RateLimitMiddleware").to(RateLimitMiddleware);

// Register services that middleware depends on
container.bind<Logger>("Logger").to(ConsoleLogger);
container.bind<AuthService>("AuthService").to(JwtAuthService);
container.bind<CacheService>("CacheService").to(RedisCache);

// Create server with container
const server = new InversifyExpressServer(container);

// Configure global Express middleware
server.setConfig((app) => {
  app.use(express.json());
  app.use(express.urlencoded({ extended: true }));
  // Note: Controller/method-specific middleware is handled automatically
});

Middleware Execution Order

Middleware executes in the following order:

  1. Global app-level middleware (configured via setConfig())
  2. Controller-level middleware (applied via @controller() or @withMiddleware())
  3. Method-level middleware (applied via method decorators)
  4. Route handler (the actual controller method)
  5. Error middleware (configured via setErrorConfig())

Usage Examples:

@controller("/api/data", corsMiddleware, LoggingMiddleware) // Middleware 2 & 3
class DataController extends BaseHttpController {
  @httpGet("/")
  @withMiddleware(AuthenticationMiddleware, RateLimitMiddleware) // Middleware 4 & 5
  getData() { // Handler executes 6th
    return this.ok({ data: "sample" });
  }
}

// Execution order for GET /api/data:
// 1. Global middleware (express.json, etc.)
// 2. corsMiddleware
// 3. LoggingMiddleware  
// 4. AuthenticationMiddleware
// 5. RateLimitMiddleware
// 6. getData() method
// 7. Error middleware (if any errors occur)

Utility Functions

Utility functions for working with middleware metadata.

/**
 * Gets middleware metadata for a specific constructor and key
 * @param constructor - Target constructor
 * @param key - Metadata key
 * @returns Array of middleware for the specified key
 */
function getMiddlewareMetadata(
  constructor: DecoratorTarget,
  key: string
): Middleware[];

docs

auth.md

base-controller.md

decorators.md

index.md

middleware.md

results.md

server.md

tile.json