@soapjs/soap-express
v0.5.1
Published
HTTP-focused Express.js integration for @soapjs/soap framework with dependency injection support
Downloads
1,159
Readme
@soapjs/soap-express
HTTP-focused Express.js integration for the @soapjs/soap framework 0.6.5+ with modern dependency injection, authentication, and advanced routing capabilities.
Table of Contents
- Features
- Installation
- Quick Start
- Core Concepts
- Controllers & Routes
- Dependency Injection
- Authentication & Authorization
- CQRS & Domain Events
- Request/Response Transformation
- Advanced Routing
- Middleware System
- Error Handling
- Best Practices
- API Reference
- Migration Guide
Features
- 🚀 HTTP-Only Focus: Clean separation from WebSocket functionality
- 🔧 Modern DI System: Full integration with the @soapjs/soap DI container
- 🎯 Decorator-Based: Clean, declarative API using decorators
- 🛡️ Type Safety: Full TypeScript support
- 🔐 Authentication: Built-in auth decorators and middleware factory
- 📨 CQRS & Events:
@CommandHandler/@QueryHandler/@EventHandlerwith bus wiring - 📦 Middleware Support: Built-in middleware for validation, CORS, rate limiting, etc.
- 🔄 RouteIO: Request/response transformation system
- ⚡ Express Integration: Seamless integration with Express.js
- 🎨 Flexible Architecture: Use DI with decorators or direct container access
- 🔌 Plugin System: Extensible plugin architecture
- 📚 Auto Documentation: Automatic API documentation generation
Installation
npm install @soapjs/soap-express @soapjs/soap expressQuick Start
import { SoapExpressApp, Controller, Get } from '@soapjs/soap-express';
import { DI, Injectable, Inject } from '@soapjs/soap';
// Service with dependency injection
@Injectable()
class UserService {
async getUsers() {
return [{ id: 1, name: 'John Doe' }];
}
}
// Controller
@Controller('/api/users')
class UserController {
constructor(@Inject('UserService') private userService: UserService) {}
@Get('/')
async getUsers(req: any, res: any) {
const users = await this.userService.getUsers();
res.json(users);
}
}
// App setup with new DI system
const app = new SoapExpressApp();
DI.bind('UserService').toClass(UserService);
app.registerController(UserController);
await app.start(3000);Core Concepts
1. SoapExpressApp
The main application class that wraps Express.js and provides integration with @soapjs/soap.
const app = new SoapExpressApp({
container?: DIContainer, // DI container (optional, uses global by default)
errorHandler?: Function, // Custom error handler
errorHandlerOptions?: object, // Error handler options
middlewares?: any[], // Global middlewares
cors?: object, // CORS options
rateLimit?: object, // Rate limiting options
logging?: object // Logging options
});2. Controllers
Controllers are classes that handle HTTP requests. They use decorators to define routes and can inject services.
@Controller('/api/users')
class UserController {
// Route handlers here
}3. Services
Services contain business logic and can be injected into controllers.
@Injectable()
class UserService {
// Business logic here
}Controllers & Routes
Basic Route Decorators
import { Controller, Get, Post, Put, Delete, Patch } from '@soapjs/soap-express';
@Controller('/api/users')
class UserController {
@Get('/')
async getUsers(req: Request, res: Response) {
// Handle GET /api/users
}
@Get('/:id')
async getUser(req: Request, res: Response) {
// Handle GET /api/users/:id
}
@Post('/')
async createUser(req: Request, res: Response) {
// Handle POST /api/users
}
@Put('/:id')
async updateUser(req: Request, res: Response) {
// Handle PUT /api/users/:id
}
@Delete('/:id')
async deleteUser(req: Request, res: Response) {
// Handle DELETE /api/users/:id
}
}Advanced Route Options
Routes can be configured with advanced options:
@Get('/', {
cors: {
origin: ['http://localhost:3000', 'https://myapp.com'],
credentials: true
},
rateLimit: {
maxRequests: 100,
windowMs: 15 * 60 * 1000 // 15 minutes
},
cache: {
ttl: 300 // 5 minutes
},
validation: {
request: {
schema: {
name: { type: 'string', required: true },
email: { type: 'string', required: true, format: 'email' }
}
}
}
})
async getUsers(req: Request, res: Response) {
// Route with advanced options
}Dependency Injection
The framework integrates with the @soapjs/soap 0.6.5+ DI container, providing modern dependency resolution and injection with the new DI.bind().toClass() API.
Using Decorators (Recommended)
import { Injectable, Inject } from '@soapjs/soap';
@Injectable()
class UserService {
async getUsers() {
return [{ id: 1, name: 'John Doe' }];
}
}
@Injectable()
class EmailService {
async sendEmail(to: string, subject: string, body: string) {
// Send email logic
}
}
@Controller('/api/users')
class UserController {
constructor(
@Inject('UserService') private userService: UserService,
@Inject('EmailService') private emailService: EmailService
) {}
@Post('/')
async createUser(req: Request, res: Response) {
const user = await this.userService.createUser(req.body);
await this.emailService.sendEmail(user.email, 'Welcome!', 'Welcome to our app!');
res.json(user);
}
}
// Registration using new DI system
DI.bind('UserService').toClass(UserService);
DI.bind('EmailService').toClass(EmailService);Using Container Directly
import { DI } from '@soapjs/soap';
@Controller('/api/users')
class UserController {
@Get('/')
async getUsers(req: Request, res: Response) {
const userService = DI.get('UserService');
const users = await userService.getUsers();
res.json(users);
}
}
// Registration using new DI system
DI.bind('UserService').toClass(UserService);
DI.bind('config').toValue({ apiKey: 'secret' });
DI.bind('logger').toFactory(() => new Logger());Service Registration Methods
// Class registration (new DI system)
DI.bind('UserService').toClass(UserService);
// Value registration
DI.bind('config').toValue({ apiKey: 'secret' });
// Factory registration
DI.bind('logger').toFactory(() => new Logger());
// Interface binding
DI.bind('IUserRepository').toInterface(UserRepository);
// Abstract binding
DI.bind('BaseService').toAbstract(UserService);Authentication & Authorization
Auth Decorators
import { Auth, AdminOnly, RolesOnly, Public, SelfOnly } from '@soapjs/soap-express';
@Controller('/api/users')
class UserController {
@Get('/')
@Public() // No authentication required
async getUsers(req: Request, res: Response) {
// Public endpoint
}
@Get('/profile')
@Auth('jwt') // Simple strategy name
async getProfile(req: Request, res: Response) {
// Requires JWT authentication
res.json({ user: req.user });
}
@Post('/')
@Auth({
strategy: 'jwt',
roles: { allow: ['admin', 'user'] }
})
async createUser(req: Request, res: Response) {
// Requires JWT + specific roles
}
@Get('/admin')
@AdminOnly('jwt') // Requires admin role
async adminOnly(req: Request, res: Response) {
// Admin only endpoint
}
@Get('/:id')
@SelfOnly('jwt') // Only resource owner can access
async getUserById(req: Request, res: Response) {
// User can only access their own data
}
@Post('/:id/update')
@RolesOnly(['admin', 'moderator'], 'jwt') // Multiple roles
async updateUser(req: Request, res: Response) {
// Requires specific roles
}
}Auth Strategy Registration
// Auth strategies come from @soapjs/soap-auth.
// Register ALL HTTP strategies from a SoapAuth-compatible provider in one call.
// Accepts any object exposing listStrategies(type) + getStrategy(name, type),
// so soap-express needs no direct dependency on soap-auth.
app.registerAuth(soapAuth);
// ...or register a single strategy directly:
// app.registerAuthStrategy(new JWTStrategy({
// secret: process.env.JWT_SECRET || 'your-secret-key',
// algorithms: ['HS256'],
// issuer: 'your-app',
// audience: 'your-users'
// }));
// app.registerAuthStrategy(new LocalStrategy({
// usernameField: 'email',
// validateUser: async (email, password) => {
// const user = await userService.findByEmail(email);
// if (user && await bcrypt.compare(password, user.password)) {
// return user;
// }
// return null;
// }
// }));CQRS & Domain Events
soap-express ships decorators that register CQRS handlers and domain-event
consumers in the DI container at decoration time. wireCqrs (enabled via
bootstrap({ cqrs: true })) connects command/query handlers to in-memory buses.
Commands & Queries
import { CommandHandler, QueryHandler } from '@soapjs/soap-express/cqrs';
import { BaseCommand, BaseQuery } from '@soapjs/soap/cqrs';
import { Inject } from '@soapjs/soap';
export class CreateUserCommand extends BaseCommand {
constructor(public readonly email: string) { super(); }
}
@CommandHandler(CreateUserCommand)
export class CreateUserHandler {
constructor(@Inject('UserRepository') private readonly repo: UserRepository) {}
async handle(cmd: CreateUserCommand): Promise<Result<User>> { /* ... */ }
}With cqrs: true, CommandBus and QueryBus are bound in the container and
every decorated handler is registered. Controllers inject the buses and dispatch:
@Inject('CommandBus') private readonly commandBus: CommandBus;
const result = await this.commandBus.dispatch(new CreateUserCommand(email));Domain Events
import { EventHandler, IEventHandler } from '@soapjs/soap-express/cqrs';
import { BaseDomainEvent } from '@soapjs/soap/domain';
export class UserCreatedEvent extends BaseDomainEvent { /* ... */ }
@EventHandler(UserCreatedEvent)
export class SendWelcomeEmail implements IEventHandler<UserCreatedEvent> {
async handle(event: UserCreatedEvent): Promise<void> { /* ... */ }
}Multiple handlers per event (fan-out): since 0.3.1 the default DI token is
EventHandler:<eventName>:<handlerClass>, so several handlers can subscribe to the same event without colliding. (Before 0.3.1 the token was the event name alone, so a second handler silently overwrote the first.) Pass{ token }to override it.
Note:
@EventHandleronly registers handlers inDecoratorRegistry— unlikewireCqrs, soap-express does not auto-wire a domain-event bus. Dispatch is up to you: readDecoratorRegistry.getEventHandlers()and bind/subscribe them to your bus (e.g. an in-memory bus that routes by event type).
Request/Response Transformation
Using @CallUseCase
The @CallUseCase decorator allows you to delegate route handling to a use case, keeping controllers clean. Important: Use cases should follow Clean Architecture principles - they receive input and return Result<output>, never directly handle Request or Response objects.
Flow: Request → RouteIO.from() → UseCase.execute(input) → Result<output> → RouteIO.to() → Response
import { CallUseCase } from '@soapjs/soap-express';
@Injectable()
class GetUsersUseCase {
constructor(@Inject('UserService') private userService: UserService) {}
async execute(input: { page?: number; limit?: number }) {
return await this.userService.getUsers(input);
}
}
@Controller('/api/users')
class UserController {
@Get('/')
@CallUseCase(GetUsersUseCase)
@RouteIO({
from: (req: Request) => ({ page: req.query.page, limit: req.query.limit }),
to: (res: Response, result: any) => {
if (result.isSuccess()) {
res.json({ success: true, data: result.content });
} else {
res.status(500).json({ success: false, error: result.failure!.error.message });
}
}
})
async getUsers() {
// Method body ignored - RouteIO transforms Request → input,
// GetUsersUseCase.execute(input) → result, RouteIO transforms result → Response
}
}Using RouteIO for Data Transformation
RouteIO provides powerful request/response transformation:
import { RouteIO, ExpressIO } from '@soapjs/soap-express';
// 1. Using mapping functions
@Post('/')
@RouteIO({
from: (req: Request) => ({
name: req.body.name,
email: req.body.email,
// Transform request data
}),
to: (res: Response, result: any) => {
res.json({
success: true,
data: result,
timestamp: new Date().toISOString()
});
}
})
async createUser() {
// Use case receives transformed data
}
// 2. Using ExpressIO class
class UserIO implements ExpressIO {
from<T = Request>(source: T) {
const req = source as Request;
return {
name: req.body.name,
email: req.body.email,
// Custom transformation logic
};
}
to<T = Response>(result: any, target: T) {
const res = target as Response;
res.json({
success: true,
data: result,
meta: {
timestamp: new Date().toISOString(),
version: '1.0'
}
});
}
}
@Post('/')
@RouteIO(new UserIO())
async createUser() {
// Uses UserIO for transformation
}Advanced Routing
Using @soapjs/soap Route System
import { Route, GetRoute, PostRoute, RouteGroup, RouteRegistry } from '@soapjs/soap';
// Individual routes
const userRoute = new GetRoute('/api/users/:id', {
cors: { origin: true },
rateLimit: { maxRequests: 100, windowMs: 60000 },
roles: { authenticatedOnly: true }
});
const createUserRoute = new PostRoute('/api/users', {
validation: {
request: {
schema: {
name: { type: 'string', required: true },
email: { type: 'string', required: true, format: 'email' }
}
}
},
roles: { allow: ['admin', 'user'] }
});
// Route groups
const userGroup = new RouteGroup('/api/v2/users', {
cors: { origin: ['https://myapp.com'] },
rateLimit: { maxRequests: 200, windowMs: 60000 }
});
userGroup.addRoute(userRoute);
userGroup.addRoute(createUserRoute);
// Registration
app.registerRoute(userRoute);
app.registerRouteGroup(userGroup);Route Registry
import { RouteRegistry } from '@soapjs/soap';
const registry = app.getRouteRegistry();
// Add routes to registry
registry.addRoute(userRoute);
registry.addRouteGroup(userGroup);
// Get all routes
const allRoutes = registry.getAllRoutes();
// Get routes by method
const getRoutes = registry.getRoutesByMethod('GET');Middleware System
Built-in Middleware
import {
AuthMiddleware,
ValidationMiddleware,
CorsMiddleware,
RateLimitMiddleware,
LoggingMiddleware,
CacheMiddleware
} from '@soapjs/soap-express';
// Global middleware
app.use(new CorsMiddleware({
origin: ['http://localhost:3000'],
credentials: true
}));
app.use(new RateLimitMiddleware({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100 // limit each IP to 100 requests per windowMs
}));
app.use(new LoggingMiddleware({
level: 'info',
format: 'combined'
}));Custom Middleware
import { Middleware } from '@soapjs/soap-express';
class CustomMiddleware implements Middleware {
async execute(req: Request, res: Response, next: NextFunction) {
// Custom logic
console.log('Custom middleware executed');
next();
}
}
app.use(new CustomMiddleware());Route-specific Middleware
@Get('/')
@Middleware(new CustomMiddleware())
@Middleware(new ValidationMiddleware({ schema: userSchema }))
async getUsers(req: Request, res: Response) {
// Route with specific middleware
}Error Handling
Global Error Handler
const app = new SoapExpressApp({
errorHandler: (error: Error, req: Request, res: Response, next: NextFunction) => {
console.error('Global error:', error);
res.status(500).json({
error: 'Internal Server Error',
message: error.message
});
},
errorHandlerOptions: {
includeStack: process.env.NODE_ENV === 'development',
includeRequest: true
}
});Route-specific Error Handling
@Get('/')
@ErrorHandler((error: Error, req: Request, res: Response) => {
if (error.name === 'ValidationError') {
res.status(400).json({ error: 'Validation failed', details: error.message });
} else {
res.status(500).json({ error: 'Internal Server Error' });
}
})
async getUsers(req: Request, res: Response) {
// Route with custom error handling
}Use Case Error Handling
@Injectable()
class GetUsersUseCase {
async execute(input: { page?: number; limit?: number }) {
try {
return await this.userService.getUsers(input);
} catch (error) {
throw new Error(`Failed to get users: ${error.message}`);
}
}
}Best Practices
1. Service Architecture
// ✅ Good: Separate concerns
@Injectable()
class UserService {
async getUsers() {
return await this.userRepository.findAll();
}
}
@Injectable()
class UserRepository {
async findAll() {
// Database logic
}
}
// ❌ Bad: Mixed concerns
@Injectable()
class UserService {
async getUsers() {
// Database logic mixed with business logic
const users = await db.query('SELECT * FROM users');
return users.map(user => ({ ...user, fullName: `${user.firstName} ${user.lastName}` }));
}
}2. Use Case Pattern
// ✅ Good: Use cases for complex operations
@Injectable()
class CreateUserUseCase {
constructor(
@Inject('UserService') private userService: UserService,
@Inject('EmailService') private emailService: EmailService,
@Inject('ValidationService') private validationService: ValidationService
) {}
async execute(input: CreateUserInput) {
// Validate input
await this.validationService.validateUser(input);
// Create user
const user = await this.userService.createUser(input);
// Send welcome email
await this.emailService.sendWelcomeEmail(user.email);
return user;
}
}
@Controller('/api/users')
class UserController {
@Post('/')
@CallUseCase(CreateUserUseCase)
async createUser() {
// Clean controller - use case handles everything
}
}3. Error Handling
// ✅ Good: Specific error types
class UserNotFoundError extends Error {
constructor(userId: string) {
super(`User with ID ${userId} not found`);
this.name = 'UserNotFoundError';
}
}
@Injectable()
class GetUserUseCase {
async execute(input: { id: string }) {
const user = await this.userService.getUserById(input.id);
if (!user) {
throw new UserNotFoundError(input.id);
}
return user;
}
}4. Authentication
// ✅ Good: Clear auth requirements
@Controller('/api/users')
class UserController {
@Get('/')
@Public() // Explicitly public
async getUsers() { }
@Get('/profile')
@Auth('jwt') // Simple auth
async getProfile() { }
@Post('/')
@Auth({
strategy: 'jwt',
roles: { allow: ['admin', 'user'] }
}) // Complex auth
async createUser() { }
}5. Data Transformation
// ✅ Good: Use RouteIO for transformation
@Post('/')
@CallUseCase(CreateUserUseCase)
@RouteIO({
from: (req: Request) => ({
name: req.body.name,
email: req.body.email.toLowerCase().trim()
}),
to: (res: Response, result: any) => {
if (result.isSuccess()) {
res.status(201).json({
success: true,
data: result.content,
timestamp: new Date().toISOString()
});
} else {
res.status(400).json({
success: false,
error: result.failure!.error.message,
timestamp: new Date().toISOString()
});
}
}
})
async createUser() { }API Reference
SoapExpressApp
Constructor Options
interface SoapExpressOptions {
container?: DIContainer;
errorHandler?: (error: Error, req: Request, res: Response, next: NextFunction) => void;
errorHandlerOptions?: {
includeStack?: boolean;
includeRequest?: boolean;
logger?: (error: Error, req: Request, res: Response) => void;
sentry?: (error: Error, req: Request, res: Response) => void;
custom?: (error: Error, req: Request, res: Response) => void;
};
middlewares?: any[];
cors?: any;
rateLimit?: any;
logging?: any;
}Methods
registerController(controller)- Register controller(s)registerRouter(router)- Register routerregisterRoute(route)- Register individual routeregisterRouteGroup(group)- Register route groupregisterMiddleware(middleware, ready?)- Register middlewareregisterAuthStrategy(strategy)- Register a single auth strategyregisterAuth(provider)- Register all HTTP strategies from a soap-auth-compatible providergetRouteRegistry()- Get route registrygetMiddlewareRegistry()- Get middleware registrygetAuthRegistry()- Get auth registrygetAuthMiddlewareFactory()- Get auth middleware factorystart(port)- Start HTTP serverhealthCheck()- Add health check endpointgetApp()- Get Express app instancegetServer()- Get HTTP server instancegetContainer()- Get DI container
Decorators
Route Decorators
@Controller(basePath)- Mark class as controller@Get(path, options?)- GET route@Post(path, options?)- POST route@Put(path, options?)- PUT route@Delete(path, options?)- DELETE route@Patch(path, options?)- PATCH route@Head(path, options?)- HEAD route@Options(path, options?)- OPTIONS route@Trace(path, options?)- TRACE route@Connect(path, options?)- CONNECT route@All(path, options?)- All methods route
Auth Decorators
@Auth(strategy | options)- Authentication decorator@AdminOnly(strategy?)- Admin only access@RolesOnly(roles, strategy?)- Specific roles only@SelfOnly(strategy?)- Resource owner only@Public()- Public endpoint (no auth)
CQRS Decorators
@CommandHandler(commandType, options?)- Register a command handler@QueryHandler(queryType, options?)- Register a query handler@EventHandler(eventType, options?)- Register a domain-event handler (pass{ token }for multiple handlers per event)
Other Decorators
@CallUseCase(useCaseClass)- Delegate to use case@RouteIO(ioOrMapping)- Request/response transformation@Middleware(middleware)- Route-specific middleware@ErrorHandler(handler)- Route-specific error handling
Types
// Auth types (from @soapjs/soap)
interface AuthUser {
id: string | number;
email?: string;
username?: string;
roles?: string[];
permissions?: string[];
[key: string]: any;
}
interface AuthRequest extends Request {
user?: AuthUser;
auth?: {
token?: string;
type?: string;
payload?: any;
};
session?: any;
sessionID?: string;
}
interface RoleConfig {
authenticatedOnly?: boolean;
allow?: string[];
deny?: string[];
selfOnly?: boolean | ((user: AuthUser, resourceId: string) => boolean);
customCheck?: (user: AuthUser, req: AuthRequest) => boolean | Promise<boolean>;
}
// Route options
interface RouteAdditionalOptions {
cors?: RouteCorsOptions;
security?: RouteSecurityOptions;
rateLimit?: RouteRateLimitOptions;
validation?: RouteValidationOptions;
session?: any;
cache?: any;
logging?: any;
analytics?: any;
audit?: any;
roles?: RoleConfig;
middlewares?: any;
compression?: any;
}Migration Guide
From Previous Versions
Removed Features
- WebSocket support (moved to @soapjs/soap-node-socket)
- Socket.IO integration
- WebSocket decorators and controllers
- Old DI system (
DI.registerClass,DI.registerValue, etc.)
New Features in 0.6.5+
- Modern DI System: New
DI.bind().toClass()API - Plugin System: Extensible plugin architecture
- CQRS Decorators: Command, Query, Event handlers
- Auto Documentation: Automatic API documentation generation
- Enhanced Type Safety: Full TypeScript support
- Better Error Handling: Improved error management
- HTTP-only Focus: Better performance
- Auth Decorators: Built-in authentication
- Advanced Routing: @soapjs/soap Route system integration
DI System Migration
// Old way (deprecated)
DI.registerClass(UserService, 'UserService', { scope: Scope.SINGLETON });
DI.registerValue('API_KEY', 'your-api-key');
DI.registerFactory('Database', () => new Database());
// New way (0.6.5+)
DI.bind('UserService').toClass(UserService, { scope: Scope.SINGLETON });
DI.bind('API_KEY').toValue('your-api-key');
DI.bind('Database').toFactory(() => new Database());Breaking Changes
SoapExpressOptions.containeris now optional (uses global container by default)- WebSocket-related methods removed from
SoapExpressApp - WebSocket decorators removed
- Auth system refactored to use @soapjs/soap interfaces
Metrics System
The framework includes a built-in metrics system that's exporter-agnostic. You can use any metrics client by implementing the MetricsClient interface.
Basic Usage
import { SoapExpressApp, MetricsConfig, ConsoleMetricsClient } from '@soapjs/soap-express';
const app = new SoapExpressApp({});
// Enable built-in metrics
app.useMetrics({
enabled: true,
metrics: {
responseTime: true,
requestCount: true,
errorRate: true,
memoryUsage: true,
cpuUsage: true
},
client: new ConsoleMetricsClient(), // or your custom client
collectInterval: 30000, // 30 seconds
customLabels: {
service: 'my-api',
version: '1.0.0'
}
});Custom Metrics Client
import { MetricsClient } from '@soapjs/soap-express';
class PrometheusClient implements MetricsClient {
counter(name: string, value: number = 1, labels?: Record<string, string | number>): void {
// Your Prometheus implementation
}
histogram(name: string, value: number, labels?: Record<string, string | number>): void {
// Your Prometheus implementation
}
gauge(name: string, value: number, labels?: Record<string, string | number>): void {
// Your Prometheus implementation
}
summary(name: string, value: number, labels?: Record<string, string | number>): void {
// Your Prometheus implementation
}
}
app.useMetrics({
enabled: true,
metrics: {
responseTime: true,
requestCount: true,
errorRate: true,
memoryUsage: true,
cpuUsage: true
},
client: new PrometheusClient()
});Custom Metrics
// Get the metrics collector
const collector = app.getMetricsCollector();
// Record custom metrics
collector!.counter('api_requests_total', 1, { method: 'GET', route: '/api/users' });
collector!.histogram('response_time_seconds', 0.5, { route: '/api/users' });
collector!.gauge('active_connections', 25);
collector!.summary('database_query_time', 0.1, { table: 'users' });Built-in Metrics
The system automatically collects:
- Response Time:
http_request_duration_seconds(histogram) - Request Count:
http_requests_total(counter) - Error Rate:
http_errors_total(counter) - Memory Usage:
process_memory_usage_bytes,process_memory_total_bytes(gauges) - CPU Usage:
process_cpu_usage_microseconds(gauge)
Memory Monitoring System
The framework includes a comprehensive memory monitoring system with automatic leak detection and threshold monitoring.
Basic Usage
import { SoapExpressApp, MemoryMonitoringConfig } from '@soapjs/soap-express';
const app = new SoapExpressApp({});
// Enable memory monitoring
app.useMemoryMonitoring({
enabled: true,
interval: 30000, // Check every 30s
threshold: {
used: 512 * 1024 * 1024, // 512MB
percentage: 80, // 80%
heapUsed: 256 * 1024 * 1024, // 256MB
rss: 512 * 1024 * 1024 // 512MB
},
leakDetection: {
enabled: true,
consecutiveGrowths: 3,
growthThreshold: 10, // 10% growth
maxHistory: 20
},
onLeak: (info) => {
console.warn('Memory leak detected:', info);
// Auto-restart or alert
},
onThreshold: (info) => {
console.warn('Memory threshold exceeded:', info);
}
});Simple Configuration
import { createMemoryConfig } from '@soapjs/soap-express';
// Use helper function for simple setup
const config = createMemoryConfig({
threshold: '512MB',
interval: 30000,
onLeak: (info) => {
console.warn('Memory leak detected:', info.severity);
}
});
app.useMemoryMonitoring(config);Memory Monitoring Features
- Automatic Leak Detection: Detects memory leaks based on consecutive growth patterns
- Threshold Monitoring: Alerts when memory usage exceeds configured limits
- Memory History: Tracks memory usage over time
- Severity Levels: Categorizes leaks as low, medium, high, or critical
- Custom Labels: Add custom labels for better monitoring context
- Force GC: Option to force garbage collection when issues are detected
Memory Information
The system provides detailed memory information:
const monitor = app.getMemoryMonitor();
const stats = monitor!.getStats();
const summary = monitor!.getSummary();
console.log('Current memory:', stats.current);
console.log('Memory status:', summary.status);
console.log('Detected leaks:', summary.leaks);Memory Thresholds
You can set thresholds for different memory metrics:
- Used Memory: Total memory used by the process
- Memory Percentage: Percentage of total system memory
- Heap Used: Memory used by the JavaScript heap
- RSS: Resident Set Size (physical memory)
Leak Detection
The system detects memory leaks by monitoring consecutive memory growth patterns:
- Consecutive Growths: Number of consecutive growths to trigger detection
- Growth Threshold: Minimum growth percentage to consider
- Severity Levels: Automatic severity classification based on growth rate
Security System
The framework includes a comprehensive security system with built-in protection against common web vulnerabilities, implemented without external dependencies.
Basic Usage
import { SoapExpressApp, SecurityConfig } from '@soapjs/soap-express';
const app = new SoapExpressApp({});
// Enable security features
app.useSecurity({
enabled: true,
headers: {
enabled: true,
headers: {
contentSecurityPolicy: "default-src 'self'",
frameOptions: 'DENY',
contentTypeOptions: true,
xssProtection: '1; mode=block',
referrerPolicy: 'strict-origin-when-cross-origin',
strictTransportSecurity: 'max-age=31536000; includeSubDomains'
}
},
csrf: {
enabled: true,
secret: 'your-secret-key-change-in-production',
cookieName: '_csrf',
cookieOptions: {
httpOnly: true,
secure: false, // Set to true in production with HTTPS
sameSite: 'strict',
maxAge: 3600000 // 1 hour
}
},
sanitization: {
enabled: true,
options: {
stripHtml: true,
escapeSql: true,
escapeHtml: true,
preventPathTraversal: true,
validateFileUploads: true,
maxFileSize: 10 * 1024 * 1024, // 10MB
allowedMimeTypes: ['image/jpeg', 'image/png', 'image/gif']
}
}
});Security Headers
The system automatically sets security headers to protect against common attacks:
- Content Security Policy (CSP): Prevents XSS attacks
- X-Frame-Options: Prevents clickjacking
- X-Content-Type-Options: Prevents MIME type sniffing
- X-XSS-Protection: Enables browser XSS filtering
- Referrer-Policy: Controls referrer information
- Strict-Transport-Security (HSTS): Enforces HTTPS
- Permissions-Policy: Controls browser features
- Cross-Origin Policies: Controls cross-origin requests
CSRF Protection
Built-in CSRF protection without external dependencies:
// CSRF token is automatically generated and validated
app.getApp().post('/api/users', (req, res) => {
// CSRF token is available in res.locals.csrfToken
res.json({ csrfToken: res.locals.csrfToken });
});
// Get CSRF token endpoint
app.getApp().get('/api/csrf-token', (req, res) => {
const securityMiddleware = app.getSecurityMiddleware();
const token = securityMiddleware!.getCSRFMiddleware().generateToken();
res.json({ csrfToken: token });
});Input Sanitization
Automatic sanitization of all request data:
// HTML sanitization
app.getApp().post('/api/content', (req, res) => {
// req.body is automatically sanitized
// <script> tags are stripped, HTML entities are escaped
res.json({ content: req.body.content });
});
// Custom sanitizers
app.useSecurity({
sanitization: {
enabled: true,
options: {
stripHtml: true,
escapeHtml: true,
preventPathTraversal: true
},
customSanitizers: {
'email': (value) => value.toLowerCase().trim(),
'phone': (value) => value.replace(/[^\d+\-\(\)\s]/g, ''),
'username': (value) => value.replace(/[^a-zA-Z0-9_-]/g, '')
}
}
});Security Presets
Pre-configured security levels:
import { securityPresets } from '@soapjs/soap-express';
// Strict security for production
app.useSecurity({
headers: securityPresets.strict,
csrf: { enabled: true, secret: 'production-secret' },
sanitization: { enabled: true, options: { stripHtml: true } }
});
// Balanced security for development
app.useSecurity({
headers: securityPresets.balanced,
csrf: { enabled: false },
sanitization: { enabled: true, options: { stripHtml: false } }
});
// Minimal security
app.useSecurity({
headers: securityPresets.minimal,
csrf: { enabled: false },
sanitization: { enabled: false }
});Security Monitoring
Track security violations and get statistics:
const securityMiddleware = app.getSecurityMiddleware();
// Get security violations
const violations = securityMiddleware.getSecurityViolations();
// Get security statistics
const stats = securityMiddleware.getSecurityStats();
// Clear violations
securityMiddleware.clearViolations();Security Endpoints
Built-in security endpoints for monitoring:
import { createSecurityEndpoints } from '@soapjs/soap-express';
const securityMiddleware = app.getSecurityMiddleware();
const endpoints = createSecurityEndpoints(securityMiddleware);
// Security status endpoint
app.getApp().get('/security/status', endpoints.status);
// CSRF token endpoint
app.getApp().get('/security/csrf-token', endpoints.csrfToken);
// Security violations endpoint
app.getApp().get('/security/violations', endpoints.violations);File Upload Security
Automatic validation of file uploads:
app.useSecurity({
sanitization: {
enabled: true,
options: {
validateFileUploads: true,
maxFileSize: 10 * 1024 * 1024, // 10MB
allowedMimeTypes: ['image/jpeg', 'image/png', 'image/gif', 'application/pdf']
}
}
});
// File uploads are automatically validated
app.getApp().post('/api/upload', (req, res) => {
// req.files is validated and sanitized
res.json({ files: req.files });
});Security Features
- No External Dependencies: Built using only Node.js built-in modules
- Automatic Protection: All requests are automatically protected
- Configurable: Fine-grained control over security features
- Monitoring: Track security violations and statistics
- Presets: Pre-configured security levels for different environments
- Custom Sanitizers: Add your own sanitization logic
- File Upload Validation: Automatic validation of uploaded files
- CSRF Protection: Built-in CSRF token generation and validation
WebSocket Support
For WebSocket functionality, use the separate package:
npm install @soapjs/soap-node-socketThis provides Socket.IO and WebSocket support with the same clean architecture.
License
MIT
