@ticatec/common-express-server
v0.5.5
Published
Enterprise-grade TypeScript framework for Express.js with built-in multi-tenant support, CRUD controllers, validation, authentication, and API gateway integration. Simplifies building scalable REST APIs with boilerplate-free routing, automatic error handl
Keywords
Readme
@ticatec/common-express-server
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
Documentation
- Controller Guide - Comprehensive guide on using controllers for CRUD and search operations
Installation
npm install @ticatec/common-express-serverPeer Dependencies
npm install express@^5.1.0Quick Start
1. Create a Basic Server
import { BaseServer } from '@ticatec/common-express-server';
class MyServer extends BaseServer {
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(): Promise<void> {
// Set up your routes here
await this.bindRoutes('/users', () => import('./routes/UserRoutes'));
}
}
// Start the server
const server = new MyServer();
BaseServer.startup(server);2. Create Routes
import { CommonRoutes, routerHelper } from '@ticatec/common-express-server';
class UserRoutes extends CommonRoutes {
// Load additional user data
protected getUserHook(): ((user: any) => any) | null {
return async (user) => {
// Load user preferences
user.preferences = await loadPreferences(user.accountCode);
return user;
};
}
protected bindRoutes() {
this.get('/profile', routerHelper.invokeRestfulAction(this.getProfile));
this.post('/update', routerHelper.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 Hook
The getUserHook() method allows you to process and enrich user data after authentication:
import { CommonRoutes } from '@ticatec/common-express-server';
class AdminRoutes extends CommonRoutes {
// Process and enrich user data
protected getUserHook(): ((user: any) => any) | null {
return async (user) => {
if (user) {
// Load admin-specific data
user.adminData = await loadAdminData(user.accountCode);
user.permissions = await loadPermissions(user.accountCode);
user.settings = await loadSettings(user.accountCode);
}
return user;
};
}
protected bindRoutes() {
this.get('/dashboard', routerHelper.invokeRestfulAction(this.getDashboard));
}
private getDashboard = async (req: Request) => {
// User data is already enriched here
return {
dashboard: req['user'].adminData,
permissions: req['user'].permissions
};
};
}Custom User Validation
The isValidUser() method allows you to implement custom validation logic beyond authentication:
import { CommonRoutes } from '@ticatec/common-express-server';
class VerifiedUserRoutes extends CommonRoutes {
// Validate user account status
protected async isValidUser(user: any): Promise<boolean> {
if (!user) {
return false;
}
// Check if user account is active
const account = await database.getAccount(user.accountCode);
return account && account.status === 'active';
}
protected bindRoutes() {
this.get('/profile', routerHelper.invokeRestfulAction(this.getProfile));
}
private getProfile = async (req: Request) => {
return req['user'];
};
}More examples:
// Check user roles
class AdminRoutes extends CommonRoutes {
protected isValidUser(user: any): boolean {
return user && user.roles && user.roles.includes('admin');
}
}
// Tenant validation
class TenantRoutes extends CommonRoutes {
protected async isValidUser(user: any): Promise<boolean> {
if (!user || !user.tenant) {
return false;
}
const tenant = await database.getTenant(user.tenant.code);
return tenant && tenant.isActive;
}
}Custom Authentication Middleware
Use getGlobalHandler() to add custom middleware:
import { CommonRoutes, routerHelper } from '@ticatec/common-express-server';
class ApiRoutes extends CommonRoutes {
// Add custom global middleware
protected getGlobalHandler(): RequestHandler | null {
return async (req, res, next) => {
// Check API version
const version = req.headers['api-version'];
if (!version) {
throw new Error('API version is required');
}
next();
};
}
protected bindRoutes() {
this.get('/data', routerHelper.invokeRestfulAction(this.getData));
}
private getData = async (req: Request) => {
return { data: [] };
};
}Public Routes (No Authentication)
import { CommonRoutes, routerHelper } from '@ticatec/common-express-server';
class PublicRoutes extends CommonRoutes {
// Override isValidUser to allow public access (no authentication required)
protected isValidUser(user: any): boolean {
return true; // Allow access without authentication
}
protected bindRoutes() {
this.get('/info', routerHelper.invokeRestfulAction(this.getInfo));
}
private getInfo = async (req: Request) => {
return { message: 'Public information' };
};
}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
- Global user parsing (non-invasive, for all requests)
Global Middleware Order:
1. SetNoCache - Disable caching
2. HealthCheck - /health-check endpoint
3. RetrieveUser (Global) - Parse user from headers (non-invasive)
4. Routes - All route definitions
5. ActionNotFound - 404 handler
6. Error Handler - Error handlingRouterHelper (Singleton)
Middleware utilities for:
- JSON response formatting
- Cache control
- User authentication
- Error handling
- Request logging
Usage:
import { routerHelper } from '@ticatec/common-express-server';
// Use middleware
routerHelper.setNoCache // Disable caching
routerHelper.checkLoggedUser() // Require authentication
routerHelper.retrieveUser() // Parse user (non-invasive)
routerHelper.actionNotFound() // 404 handler
routerHelper.invokeRestfulAction() // Wrap async handlers
routerHelper.invokeController() // Wrap controller handlersCommonRoutes
Base class for route definitions with:
- Express router integration
- Flexible authentication control
- Custom user validation checks
- User hook support
- Global middleware support
- Logging capabilities
- Built-in HTTP method helpers
Middleware Order:
1. getUserHook() - Process and enrich user data
2. isValidUser() - Custom user validation
3. getGlobalHandler() - Custom middleware
4. bindRoutes() - Route definitionsKey Methods:
getUserHook(): ((user: any) => any) | null- Process user dataisValidUser(user: any): boolean | Promise<boolean>- Custom user validationgetGlobalHandler(): RequestHandler | null- Custom middlewarebindRoutes()- Define your routes
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
Architecture Overview
Request Processing Flow
┌─────────────────────────────────────────────────────────────┐
│ BaseServer Middleware │
├─────────────────────────────────────────────────────────────┤
│ 1. SetNoCache - Disable caching │
│ 2. HealthCheck - /health-check endpoint │
│ 3. RetrieveUser (Global) - Parse user from headers │
│ 4. Routes - All route definitions │
│ 5. ActionNotFound - 404 handler │
│ 6. Error Handler - Error handling │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ CommonRoutes Middleware Order │
├─────────────────────────────────────────────────────────────┤
│ 1. getUserHook() - Process user data │
│ 2. isValidUser() - Custom user validation │
│ 3. getGlobalHandler() - Custom middleware │
│ 4. bindRoutes() - Route definitions │
└─────────────────────────────────────────────────────────────┘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 HeadersGateway Responsibilities
The API Gateway should:
- Authenticate requests using JWT tokens, session cookies, or other authentication mechanisms
- Extract user information from the authentication token/session
- Forward user data as HTTP headers to the Express application
- 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'
}User Impersonation
The library supports user impersonation for debugging and troubleshooting:
// 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'
}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>;
// User interfaces
export interface CommonUser {
accountCode: string;
name: string;
tenant?: { // Optional, may not be present for platform admins
code: string;
name: string;
};
[key: string]: any;
}
export interface LoggedUser extends CommonUser {
isPlatform?: boolean; // Platform admin flag
actAs?: CommonUser; // For user impersonation
}Development
Build
npm run build # Build the project
npm run dev # Development mode with watchRequirements
- 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 utilitieslog4js: Logging framework
Contributing
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
License
This project is licensed under the MIT License - see the LICENSE file for details.
Support
For support and questions:
- 📧 Email: [email protected]
- 🐛 Issues: GitHub Issues
- 📚 Documentation: GitHub Repository
Made with ❤️ by TicaTec
