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

@ticatec/common-express-server

v0.3.1

Published

A comprehensive TypeScript library providing common classes, controllers, and middleware for building scalable Express.js applications with multi-tenant support.

Readme

@ticatec/common-express-server

npm version License: MIT

A comprehensive TypeScript library providing common classes, controllers, and middleware for building scalable Express.js applications with multi-tenant support.

中文 | English

Features

  • 🚀 Express.js Foundation: Built on Express.js 5.x with full TypeScript support
  • 🏢 Multi-tenant Architecture: Built-in support for multi-tenant applications
  • 🔐 Authentication & Authorization: User authentication and role-based access control
  • 🎯 Controller Patterns: Pre-built base controllers for common CRUD operations
  • 📝 Validation: Integrated data validation using bean-validator
  • 🔄 Error Handling: Centralized error handling and logging
  • 🌐 Internationalization: Built-in language support via headers
  • 📊 Logging: Structured logging with log4js integration
  • 🎨 TypeScript First: Full TypeScript support with comprehensive type definitions

Installation

npm install @ticatec/common-express-server

Peer Dependencies

npm install express@^5.1.0

Quick Start

1. Create a Basic Server

import { BaseServer, CommonRouterHelper } from '@ticatec/common-express-server';

class MyRouterHelper extends CommonRouterHelper {
    // Add custom middleware or override methods as needed
}

class MyServer extends BaseServer<MyRouterHelper> {
    protected getHelper(): MyRouterHelper {
        return new MyRouterHelper();
    }

    protected async loadConfigFile(): Promise<void> {
        // Load your configuration here
        console.log('Loading configuration...');
    }

    protected getWebConf() {
        return {
            port: 3000,
            ip: '0.0.0.0',
            contextRoot: '/api'
        };
    }

    protected async setupRoutes(app: Express): Promise<void> {
        // Set up your routes here
        await this.bindRoutes(app, '/users', () => import('./routes/UserRoutes'));
    }
}

// Start the server
const server = new MyServer();
BaseServer.startup(server);

2. Create Routes

import { CommonRoutes, CommonRouterHelper } from '@ticatec/common-express-server';

class UserRoutes extends CommonRoutes<CommonRouterHelper> {
    constructor(helper: CommonRouterHelper) {
        super(helper); // checkUser = true by default
        this.setupRoutes();
    }

    private setupRoutes() {
        this.router.get('/profile', this.helper.invokeRestfulAction(this.getProfile));
        this.router.post('/update', this.helper.invokeRestfulAction(this.updateProfile));
    }

    private getProfile = async (req: Request) => {
        // Your logic here
        return { message: 'User profile' };
    };

    private updateProfile = async (req: Request) => {
        // Your logic here
        return { message: 'Profile updated' };
    };
}

export default UserRoutes;

Custom User Authentication

You can provide a custom user verification function to the CommonRoutes constructor:

import { CommonRoutes, CommonRouterHelper, UserChecker } from '@ticatec/common-express-server';

// Custom user checker function
const customUserChecker: UserChecker = (req: Request): boolean => {
    // Your custom authentication logic
    const userRole = req.headers['user-role'];
    return userRole === 'admin' || userRole === 'moderator';
};

class AdminRoutes extends CommonRoutes<CommonRouterHelper> {
    constructor(helper: CommonRouterHelper) {
        // Use custom user checker instead of default
        super(helper, customUserChecker);
        this.setupRoutes();
    }

    private setupRoutes() {
        this.router.get('/dashboard', this.helper.invokeRestfulAction(this.getDashboard));
    }

    private getDashboard = async (req: Request) => {
        return { message: 'Admin dashboard' };
    };
}

// Skip authentication entirely
class PublicRoutes extends CommonRoutes<CommonRouterHelper> {
    constructor(helper: CommonRouterHelper) {
        super(helper, false); // No authentication check
        this.setupRoutes();
    }

    private setupRoutes() {
        this.router.get('/info', this.helper.invokeRestfulAction(this.getInfo));
    }

    private getInfo = async (req: Request) => {
        return { message: 'Public information' };
    };
}

The checkUser parameter accepts three types:

  • true (default): Uses the default helper.checkLoggedUser() middleware
  • false: Skips user authentication entirely
  • UserChecker function: A custom function (req: Request) => boolean that returns true if authenticated

3. Create Controllers

import { TenantBaseController } from '@ticatec/common-express-server';
import { ValidationRules, StringValidator } from '@ticatec/bean-validator';

interface UserService {
    createNew(user: any, data: any): Promise<any>;
    update(user: any, data: any): Promise<any>;
    search(user: any, query: any): Promise<any>;
}

const userValidationRules: ValidationRules = [
    new StringValidator('name', { required: true, minLen: 2 }),
    new StringValidator('email', {
        required: true,
        format: {
            regex: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
            message: 'Invalid email format'
        }
    })
];

class UserController extends TenantBaseController<UserService> {
    constructor(userService: UserService) {
        super(userService, userValidationRules);
    }

    // CRUD methods are inherited and automatically validated
    // createNew(), update(), del() are available

    // Add custom methods
    search() {
        return async (req: Request): Promise<any> => {
            const query = req.query;
            this.checkInterface('search');
            return await this.invokeServiceInterface('search', [
                this.getLoggedUser(req), 
                query
            ]);
        };
    }
}

Core Classes

BaseServer

Abstract base server class that provides:

  • Express application setup
  • Configuration loading
  • Route binding
  • Error handling
  • Health check endpoint
  • Static file serving

CommonRouterHelper

Middleware utilities for:

  • JSON response formatting
  • Cache control
  • User authentication
  • Error handling
  • Request logging

CommonRoutes

Base class for route definitions with:

  • Express router integration
  • User authentication checks
  • Logging capabilities

Controllers Hierarchy

  • BaseController: Basic controller with logging and user context
  • CommonController: CRUD operations with validation
  • AdminBaseController: Admin-specific operations (tenant-independent)
  • TenantBaseController: Tenant-specific operations
  • AdminSearchController: Admin search operations
  • TenantSearchController: Tenant search operations

Configuration

Application Configuration

import { AppConf } from '@ticatec/common-express-server';

// Initialize configuration
AppConf.init({
    database: {
        host: 'localhost',
        port: 5432
    },
    server: {
        port: 3000
    }
});

// Use configuration
const config = AppConf.getInstance();
const dbHost = config.get('database.host');
const serverPort = config.get('server.port');

Gateway Architecture

This application is designed to work behind an API Gateway. The gateway handles JWT tokens or session-based authentication and forwards the authenticated user information to the Express application via HTTP headers.

Architecture Flow

Client Request (JWT/Session) → API Gateway → Express Application
                                    ↓
                            User Info Headers

Gateway Responsibilities

The API Gateway should:

  1. Authenticate requests using JWT tokens, session cookies, or other authentication mechanisms
  2. Extract user information from the authentication token/session
  3. Forward user data as HTTP headers to the Express application
  4. Handle authorization and rate limiting as needed

User Authentication Headers

The library expects user information in the request headers:

// Headers forwarded by the gateway
{
    'user': encodeURIComponent(JSON.stringify({
        accountCode: 'user123',
        name: 'John Doe',
        tenant: { code: 'tenant1', name: 'Tenant One' }
    })),
    'x-language': 'en'
}

Gateway Implementation Example

Here's an example of how the gateway might process authentication:

// Gateway middleware (pseudo-code)
async function processAuthentication(request) {
    // 1. Validate JWT token or session
    const token = request.headers.authorization?.replace('Bearer ', '');
    const userInfo = await validateJWT(token);
    
    // 2. Extract user information
    const user = {
        accountCode: userInfo.sub,
        name: userInfo.name,
        tenant: {
            code: userInfo.tenant_code,
            name: userInfo.tenant_name
        }
    };
    
    // 3. Forward to Express app with user header
    request.headers['user'] = encodeURIComponent(JSON.stringify(user));
    request.headers['x-language'] = userInfo.preferred_language || 'en';
    
    // 4. Proxy request to Express application
    return proxyToExpressApp(request);
}

Security Considerations

  • No direct authentication: This Express application does NOT handle JWT validation or session management
  • Trust boundary: The application trusts that the gateway has properly authenticated users
  • Header validation: User headers are parsed and validated but not authenticated
  • Network security: Ensure secure communication between gateway and Express app (internal network/VPN)

User Impersonation

The library supports user impersonation, which allows privileged system users to act as another user (including cross-tenant operations) for debugging and troubleshooting purposes.

How It Works

When a privileged user needs to impersonate another user, the gateway should include both the original user and the target user in the headers:

// Headers with user impersonation
{
    'user': encodeURIComponent(JSON.stringify({
        // Original privileged user
        accountCode: 'admin123',
        name: 'System Admin',
        tenant: { code: 'system', name: 'System Tenant' },
        
        // User being impersonated
        actAs: {
            accountCode: 'user456',
            name: 'Target User',
            tenant: { code: 'client-a', name: 'Client A' }
        }
    })),
    'x-language': 'en'
}

Implementation in Controllers

The BaseController.getLoggedUser() method automatically handles impersonation:

class MyController extends TenantBaseController<MyService> {
    
    async getUserData(req: Request) {
        // This will return the impersonated user if actAs is present,
        // otherwise returns the original user
        const currentUser = this.getLoggedUser(req);
        
        console.log('Operating as:', currentUser.name);
        console.log('Tenant context:', currentUser.tenant.code);
        
        // All operations will be performed in the context of the impersonated user
        return await this.service.getUserData(currentUser);
    }
}

Use Cases

  • Debug user issues: Support staff can reproduce problems by acting as the affected user
  • Cross-tenant troubleshooting: System administrators can debug issues across different tenants
  • Testing user permissions: Verify that user-specific access controls work correctly
  • Data migration: Perform operations on behalf of users during system migrations

Express Application Responsibilities

The Express application simply:

  • Trusts the gateway: Accepts user impersonation information from authenticated gateway requests
  • Processes context: Uses the actAs user for all business operations when present
  • No validation: Does not validate impersonation privileges or restrictions

Gateway Responsibilities for Impersonation

All impersonation controls should be handled by the gateway:

  • Privilege validation: Verify users have permission to impersonate others
  • Audit logging: Record all impersonation activities for security auditing
  • Time limits: Implement time-based restrictions on impersonation sessions
  • Session management: Handle impersonation session lifecycle
  • Cross-tenant controls: Apply additional checks for cross-tenant impersonation
  • Notification: Optionally notify target users when their accounts are being impersonated

Multi-tenant Support

The library provides built-in multi-tenant support:

// Tenant-specific controller
class ProductController extends TenantBaseController<ProductService> {
    // Automatically receives logged user context
    // All operations are tenant-scoped
}

// Admin controller (cross-tenant)
class SystemController extends AdminBaseController<SystemService> {
    // Operations across all tenants
}

Validation

Built-in validation using @ticatec/bean-validator:

import { ValidationRules, StringValidator, NumberValidator } from '@ticatec/bean-validator';

const rules: ValidationRules = [
    new StringValidator('name', { required: true, minLen: 2, maxLen: 50 }),
    new StringValidator('email', {
        required: true,
        format: {
            regex: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
            message: 'Invalid email format'
        }
    }),
    new NumberValidator('age', { required: false, minValue: 18, maxValue: 120 })
];

class UserController extends CommonController<UserService> {
    constructor(service: UserService) {
        super(service, rules); // Validation applied automatically
    }
}

Error Handling

Centralized error handling with @ticatec/express-exception:

import { 
    ActionNotFoundError, 
    UnauthenticatedError, 
    IllegalParameterError 
} from '@ticatec/express-exception';

// Errors are automatically handled and formatted
throw new ActionNotFoundError('Resource not found');
throw new UnauthenticatedError('User not authenticated');
throw new IllegalParameterError('Invalid input data');

API Reference

Types

// Function signatures
export type RestfulFunction = (req: Request) => any;
export type ControlFunction = (req: Request, res: Response) => any;
export type moduleLoader = () => Promise<any>;

// Validation types (from @ticatec/bean-validator)
export type ValidationRules = Array<BaseValidator>;

// User interfaces
export interface CommonUser {
    accountCode: string;
    name: string;
    tenant: {
        code: string;
        name: string;
    };
    [key: string]: any;
}

export interface LoggedUser extends CommonUser {
    actAs?: CommonUser; // For user impersonation
}

Development

Build

npm run build         # Build the project
npm run dev           # Development mode with watch

Requirements

  • Node.js >= 18.0.0
  • Express.js ^5.1.0
  • TypeScript ^5.0.0

Dependencies

  • @ticatec/bean-validator: Data validation
  • @ticatec/express-exception: Error handling
  • @ticatec/node-common-library: Common utilities
  • log4js: Logging framework

Contributing

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

License

This project is licensed under the MIT License - see the LICENSE file for details.

Support

For support and questions:


Made with ❤️ by TicaTec