bashtech-express-ts-middleware
v1.2.9
Published
Middleware for BashTech's TS-Node API
Readme
bashtech-express-ts-middleware
A powerful, production-ready Express middleware library for TypeScript/Node.js applications that provides comprehensive authentication, authorization, rate limiting, data validation, and audit trail functionality.
📋 Table of Contents
- Features
- Installation
- Requirements
- Quick Start
- Core Features
- Advanced Configuration
- File Size Utilities
- Rate Limit Utilities
- TypeScript Support
- Security Features
- Testing
- Error Handling
- Contributing
- License
- Acknowledgments
- Support
- Roadmap
🚀 Features
- 🔐 JWT Authentication - Secure token-based authentication with multiple token types support
- 👮 Role-Based Access Control (RBAC) - Fine-grained permission management with policies and resources
- 🚦 Rate Limiting - Redis-based rate limiting with user-friendly helper utilities
- ✅ Data Validation - Comprehensive request validation for body, query, params, headers, and files
- 📁 File Upload Validation - Advanced file validation with size, type, extension checks
- 📝 Audit Trail - Automatic audit logging for all authenticated admin actions
- 🔄 Request Caching - Redis-based response caching for GET requests
- 🎣 Webhook Support - Built-in Stripe and Paystack webhook middleware
- 🔑 Redis Key Prefixing - Configurable Redis key prefixes to avoid conflicts between multiple app instances
- 🛡️ Security First - Built with security best practices in mind
- 📦 TypeScript Native - Full TypeScript support with comprehensive type definitions
📦 Installation
npm install bashtech-express-ts-middlewareor with yarn:
yarn add bashtech-express-ts-middleware🔧 Requirements
- Node.js >= 14.0.0
- Express >= 4.17.0
- MongoDB (via Mongoose)
- Redis
🎯 Quick Start
Basic Setup
import express from 'express';
import mongoose from 'mongoose';
import Redis from 'ioredis';
import RouteMW from 'bashtech-express-ts-middleware';
const app = express();
// Initialize MongoDB connection
const mongoConnection = await mongoose.connect('mongodb://localhost:27017/myapp');
// Initialize Redis client
const redis = new Redis({
host: 'localhost',
port: 6379
});
// Configure the middleware
RouteMW.init({
connection: mongoConnection.connection,
redis: redis,
jwt_secret: process.env.JWT_SECRET || 'your-secret-key',
// Redis key prefix (optional, defaults to 'btmw')
// Use this to avoid conflicts when multiple instances share the same Redis
redis_key_prefix: 'myapp', // All Redis keys will be prefixed with 'myapp:'
// Rate limiting defaults
global_rate_limit: 100,
global_rate_window: 1, // minutes
// Collection names (optional, these are defaults)
jwt_collection: 'jw_tokens',
policy_collection: 'policies',
resource_collection: 'resources',
user_role_collection: 'user_roles',
audit_trail_collection: 'audit_trails',
// User model callback
get_user_model: (userType) => {
// Return appropriate user model based on type
return userType === 'admin' ? AdminModel : UserModel;
},
// Response handlers
send_success_cb: (req, res, next, data, code = 200) => {
res.status(code).json({ success: true, data });
},
send_error_cb: (req, res, next, error, code = 400) => {
res.status(code).json({ success: false, error: error.message });
}
});Using Middleware in Routes
// Public route - no authentication required
app.get('/api/public',
RouteMW.attach.bind({
scope: 'anyone',
auth: false
}),
(req, res) => {
res.json({ message: 'Public endpoint' });
}
);
// Protected route - requires authentication
app.get('/api/user/profile',
RouteMW.attach.bind({
scope: 'user',
auth: true,
token_type: 'web'
}),
(req, res) => {
const user = req.getAuthUser();
res.json({ user });
}
);
// Admin route with specific permissions
app.post('/api/admin/users',
RouteMW.attach.bind({
scope: 'admin',
auth: true,
permission: {
resource: 'users',
action: 'create',
access_level: 'Write'
}
}),
(req, res) => {
// Create user logic
}
);📚 Core Features
1. Authentication & Authorization
Multiple Token Types
app.post('/api/login',
RouteMW.attach.bind({
scope: 'anyone',
auth: false,
validations: {
body: {
email: { required: true },
password: { required: true }
}
}
}),
async (req, res) => {
// Your login logic
// Generate JWT token with appropriate type
}
);
// MFA route - requires MFA token
app.post('/api/verify-mfa',
RouteMW.attach.bind({
scope: 'user',
auth: true,
token_type: 'mfa'
}),
async (req, res) => {
// MFA verification logic
}
);Permission-Based Access Control
// Define route with specific permissions
app.delete('/api/posts/:id',
RouteMW.attach.bind({
scope: 'user',
auth: true,
permission: {
resource: 'posts',
action: 'delete',
access_level: 'Delete'
}
}),
async (req, res) => {
// Delete post logic
}
);
// Multiple permissions
app.put('/api/admin/system',
RouteMW.attach.bind({
scope: 'admin',
auth: true,
permission: [
{ resource: 'system', action: 'update', access_level: 'Write' },
{ resource: 'config', action: 'modify', access_level: 'Write' }
]
}),
async (req, res) => {
// System update logic
}
);2. Data Validation
Basic Validation
import { FileSize } from 'bashtech-express-ts-middleware';
app.post('/api/users',
RouteMW.attach.bind({
scope: 'admin',
auth: true,
strict: true, // Reject unknown fields
validations: {
body: {
name: {
required: true,
validate: [(value) => value.length >= 3, 'Name must be at least 3 characters'],
sanitize: (value) => value.trim()
},
email: {
required: true,
validate: [(value) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value), 'Invalid email']
},
age: {
required: false,
default: 18,
validate: [(value) => value >= 18 && value <= 120, 'Age must be between 18 and 120']
}
}
}
}),
async (req, res) => {
// Create user with validated data
}
);Nested Object Validation
app.post('/api/products',
RouteMW.attach.bind({
scope: 'admin',
auth: true,
validations: {
body: {
name: { required: true },
price: {
required: true,
validate: [(v) => v > 0, 'Price must be positive']
},
metadata: {
required: true,
properties: {
category: { required: true },
tags: { required: false, default: [] },
specifications: {
required: false,
properties: {
weight: { required: false },
dimensions: { required: false }
}
}
}
}
}
}
}),
async (req, res) => {
// Create product
}
);Cross-Field Validation
// Validate fields based on other field values
app.post('/api/documents',
RouteMW.attach.bind({
scope: 'user',
auth: true,
validations: {
body: {
document_type: {
required: true,
validate: [(v) => ['standard', 'custom', 'other'].includes(v), 'Invalid document type']
},
description: {
required: false, // Conditionally required
validate: [
(desc: string, body: any) =>
body.document_type !== 'other' || Boolean(desc && desc.length > 0),
'Description is required for other document type',
],
sanitize: (v) => v?.trim()
},
priority: {
required: true,
validate: [
(priority: number, body: any) => {
// High priority requires admin approval
if (priority > 5 && !body.admin_approved) {
return false;
}
return priority >= 1 && priority <= 10;
},
'Invalid priority or admin approval required for high priority'
]
}
}
}
}),
async (req, res) => {
// Process document with cross-field validation
}
);Array Validation
app.post('/api/bulk-upload',
RouteMW.attach.bind({
scope: 'admin',
auth: true,
validations: {
body: {
// Array of primitives
tags: {
required: true,
items: {
validate: [(v) => typeof v === 'string' && v.length > 0, 'Invalid tag']
},
min_length: 1,
max_length: 10
},
// Array of objects
products: {
required: true,
items: {
properties: {
name: { required: true },
price: {
required: true,
validate: [(v) => v > 0, 'Price must be positive']
}
}
},
min_length: 1,
max_length: 100
}
}
}
}),
async (req, res) => {
// Process bulk upload
}
);3. File Upload Validation
Single File Upload
import { FileSize } from 'bashtech-express-ts-middleware';
app.post('/api/upload/avatar',
RouteMW.attach.bind({
scope: 'user',
auth: true,
validations: {
files: {
avatar: {
file_required: true,
size: FileSize.MB(5), // Max 5MB
min_size: FileSize.KB(10), // Min 10KB
extension: ['.jpg', '.png', '.webp'],
mime_type: 'image/*'
}
}
}
}),
async (req, res) => {
const avatar = req.files.avatar;
// Process avatar upload
}
);Multiple File Upload
app.post('/api/upload/documents',
RouteMW.attach.bind({
scope: 'user',
auth: true,
validations: {
files: {
documents: {
file_required: true,
multiple: true,
min_files: 1,
max_files: 5,
size: FileSize.MB(10),
extension: ['.pdf', '.doc', '.docx'],
mime_type: ['application/pdf', 'application/msword'],
custom_validate: (file) => {
// Custom validation logic
if (file.name.includes('confidential')) {
return [false, 'Confidential files not allowed'];
}
return true;
}
}
}
}
}),
async (req, res) => {
const documents = req.files.documents;
// Process multiple documents
}
);Using Predefined Size Limits
app.post('/api/upload/media',
RouteMW.attach.bind({
scope: 'user',
auth: true,
validations: {
files: {
thumbnail: {
file_required: true,
size: FileSize.limits.image_compressed // 2MB
},
video: {
file_required: false,
size: FileSize.limits.media_large, // 100MB
mime_type: 'video/*'
}
}
}
}),
async (req, res) => {
// Process media upload
}
);4. Rate Limiting
Global Rate Limiting
// Applied automatically to all routes
// Configured in RouteMW.init()Route-Specific Rate Limiting
// IP-based rate limiting
app.get('/api/search',
RouteMW.attach.bind({
scope: 'anyone',
auth: false,
rate_limiting: {
identity: 'ip',
window: 1, // 1 minute
limit: 10 // 10 requests per minute
}
}),
async (req, res) => {
// Search logic
}
);
// User-based rate limiting (requires auth)
app.post('/api/expensive-operation',
RouteMW.attach.bind({
scope: 'user',
auth: true,
rate_limiting: {
identity: 'userId',
window: 5, // 5 minutes
limit: 3 // 3 requests per 5 minutes
}
}),
async (req, res) => {
// Expensive operation
}
);Using RateLimit Helpers
The package provides user-friendly helper functions for common rate limiting scenarios:
import { RateLimit } from 'bashtech-express-ts-middleware';
// Per-second rate limiting
app.get('/api/realtime',
RouteMW.attach.bind({
scope: 'user',
auth: true,
rate_limiting: RateLimit.perSecond(5) // 5 requests per second
}),
async (req, res) => {
// Realtime endpoint
}
);
// Per-minute rate limiting
app.get('/api/search',
RouteMW.attach.bind({
scope: 'anyone',
auth: false,
rate_limiting: RateLimit.perMinute(30) // 30 requests per minute
}),
async (req, res) => {
// Search logic
}
);
// Per-hour rate limiting with user identity
app.post('/api/reports/generate',
RouteMW.attach.bind({
scope: 'user',
auth: true,
rate_limiting: RateLimit.perHour(10, 'userId') // 10 per hour per user
}),
async (req, res) => {
// Generate report
}
);
// Per-day rate limiting
app.post('/api/exports',
RouteMW.attach.bind({
scope: 'user',
auth: true,
rate_limiting: RateLimit.perDay(5, 'userId') // 5 exports per day
}),
async (req, res) => {
// Export data
}
);
// Custom window (in minutes)
app.post('/api/password-reset',
RouteMW.attach.bind({
scope: 'anyone',
auth: false,
rate_limiting: RateLimit.custom(15, 3) // 3 attempts per 15 minutes
}),
async (req, res) => {
// Password reset logic
}
);RateLimit Presets
Common rate limiting configurations are available as presets:
import { RateLimit } from 'bashtech-express-ts-middleware';
// Strict API rate limiting (10 req/min)
app.get('/api/premium',
RouteMW.attach.bind({
scope: 'user',
auth: true,
rate_limiting: RateLimit.presets.strict_api
}),
handler
);
// Standard API rate limiting (60 req/min)
app.get('/api/data',
RouteMW.attach.bind({
scope: 'user',
auth: true,
rate_limiting: RateLimit.presets.standard_api
}),
handler
);
// Authentication attempts (5 attempts per 15 min)
app.post('/api/login',
RouteMW.attach.bind({
scope: 'anyone',
auth: false,
rate_limiting: RateLimit.presets.auth_attempts
}),
handler
);
// Password reset (3 attempts per hour)
app.post('/api/forgot-password',
RouteMW.attach.bind({
scope: 'anyone',
auth: false,
rate_limiting: RateLimit.presets.password_reset
}),
handler
);
// File uploads (10 per hour per user)
app.post('/api/upload',
RouteMW.attach.bind({
scope: 'user',
auth: true,
rate_limiting: RateLimit.presets.file_uploads
}),
handler
);
// Available presets:
// - strict_api: 10 requests per minute (IP-based)
// - standard_api: 60 requests per minute (IP-based)
// - relaxed_api: 300 requests per minute (IP-based)
// - user_actions: 30 actions per minute (User-based)
// - auth_attempts: 5 attempts per 15 minutes (IP-based)
// - password_reset: 3 attempts per hour (IP-based)
// - file_uploads: 10 uploads per hour (User-based)
// - webhook: 1000 requests per minute (IP-based)5. Response Caching
// Cache GET requests
app.get('/api/products',
RouteMW.attach.bind({
scope: 'anyone',
auth: false,
signature: 'products_list' // Cache key prefix
}),
async (req, res) => {
// This response will be cached
const products = await Product.find();
res.json(products);
}
);
// Clear cache on updates
app.post('/api/products',
RouteMW.attach.bind({
scope: 'admin',
auth: true,
signature: 'products_list' // Will clear this cache
}),
async (req, res) => {
// Create product and clear cache
}
);6. Webhook Support
// Stripe webhook
app.post('/webhook/stripe',
RouteMW.attach.bind({
scope: 'anyone',
auth: false,
webhook: true,
webhook_service: 'stripe'
}),
async (req, res) => {
// Process Stripe webhook
}
);
// Paystack webhook
app.post('/webhook/paystack',
RouteMW.attach.bind({
scope: 'anyone',
auth: false,
webhook: true,
webhook_service: 'paystack'
}),
async (req, res) => {
// Process Paystack webhook
}
);7. Audit Trail
// Automatic audit logging for admin actions
app.delete('/api/users/:id',
RouteMW.attach.bind({
scope: 'admin',
auth: true,
permission: {
resource: 'users',
action: 'delete',
access_level: 'Delete'
},
comment: 'User deletion' // Audit trail comment
}),
RouteMW.createAuditTrail, // Add audit trail middleware
async (req, res) => {
// Delete user
res.json({ success: true });
}
);🛠️ Advanced Configuration
Custom User Population
RouteMW.init({
// ... other config
user_populate_options: [
'profile',
{ path: 'roles', populate: 'permissions' },
{ path: 'organization', select: 'name type' }
]
});Extending Middleware
RouteMW.init({
// ... other config
extend: async (req) => {
// Add custom logic that runs for every request
req.customData = await fetchCustomData();
// Add custom headers
req.headers['X-Custom-Header'] = 'value';
}
});Custom Webhook Handlers
RouteMW.init({
// ... other config
stripe_webhook_middleware: async (req) => {
// Verify Stripe signature
const signature = req.headers['stripe-signature'];
// Your verification logic
},
paystack_webhook_middleware: async (req) => {
// Verify Paystack signature
const hash = req.headers['x-paystack-signature'];
// Your verification logic
}
});📊 File Size Utilities
The package includes helpful utilities for specifying file sizes:
import { FileSize } from 'bashtech-express-ts-middleware';
// Convert units to bytes
FileSize.KB(100) // 100 KB in bytes
FileSize.MB(5) // 5 MB in bytes
FileSize.GB(1) // 1 GB in bytes
// Predefined common limits
FileSize.limits.small_text // 100KB - JSON, text files
FileSize.limits.document // 500KB - Documents, CSVs
FileSize.limits.image_compressed // 2MB - Compressed images
FileSize.limits.image_hq // 5MB - High quality images
FileSize.limits.pdf // 10MB - PDFs
FileSize.limits.media_small // 25MB - Small videos/audio
FileSize.limits.media_large // 100MB - Large videos⏱️ Rate Limit Utilities
Similar to FileSize utilities, the package provides user-friendly helpers for rate limiting:
import { RateLimit } from 'bashtech-express-ts-middleware';
// Time-based helper methods
RateLimit.perSecond(10) // 10 requests per second
RateLimit.perMinute(60) // 60 requests per minute
RateLimit.perHour(100) // 100 requests per hour
RateLimit.perDay(1000) // 1000 requests per day
RateLimit.custom(15, 5) // 5 requests per 15 minutes
// With identity specification
RateLimit.perMinute(30, 'userId') // 30 per minute per user
RateLimit.perHour(10, 'ip') // 10 per hour per IP
// Common presets for different scenarios
RateLimit.presets.strict_api // 10 req/min (IP)
RateLimit.presets.standard_api // 60 req/min (IP)
RateLimit.presets.relaxed_api // 300 req/min (IP)
RateLimit.presets.user_actions // 30 req/min (User)
RateLimit.presets.auth_attempts // 5 per 15 min (IP)
RateLimit.presets.password_reset // 3 per hour (IP)
RateLimit.presets.file_uploads // 10 per hour (User)
RateLimit.presets.webhook // 1000 req/min (IP)🔍 TypeScript Support
The package is written in TypeScript and provides comprehensive type definitions:
import RouteMW, {
FileSize,
RateLimit,
iRouteMiddleware,
iRouteWMConfig,
tRouteValidation,
tRouteFileValidation,
ePermissionAccessLevel,
eWebhookServices
} from 'bashtech-express-ts-middleware';
// Type-safe configuration
const config: iRouteWMConfig = {
// ... your config
};
// Type-safe route configuration
const routeConfig: iRouteMiddleware = {
scope: 'user',
auth: true,
// ... other options
};🔐 Security Features
- JWT Token Validation - Secure token verification with expiry checks
- Rate Limiting - Prevent abuse and DDoS attacks
- Input Validation - Prevent injection attacks
- Strict Mode - Reject unknown fields
- Audit Logging - Track all sensitive operations
- Permission Checks - Fine-grained access control
- Secure Headers - Automatic security headers
🧪 Testing
For testing, you can disable rate limiting and use test mode:
RouteMW.init({
// ... other config
is_test: process.env.NODE_ENV === 'test'
});📝 Error Handling
The middleware provides structured error responses:
// Validation Error (400)
{
"success": false,
"error": "Missing required field 'email'"
}
// Authentication Error (401)
{
"success": false,
"error": "Invalid or expired token"
}
// Authorization Error (403)
{
"success": false,
"error": "You do not have permission to this resource"
}
// Rate Limit Error (429)
{
"success": false,
"error": "Too many requests, please try again later"
}🤝 Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/AmazingFeature) - Commit your changes (
git commit -m 'Add some AmazingFeature') - Push to the branch (
git push origin feature/AmazingFeature) - Open a Pull Request
📄 License
This project is licensed under the MIT License - see the LICENSE file for details.
🙏 Acknowledgments
- Built with Express.js and TypeScript
- Redis for caching and rate limiting
- MongoDB for data persistence
- JWT for secure authentication
📞 Support
For support, email [email protected] or open an issue on GitHub.
🚧 Roadmap
- [ ] GraphQL support
- [ ] OpenAPI/Swagger documentation generation
- [ ] Built-in request logging
- [ ] Prometheus metrics integration
- [ ] WebSocket authentication
- [ ] OAuth2 provider support
- [ ] Request encryption/decryption
- [ ] API versioning support
Made with ❤️ by Bashtech (www.bashtech.solutions)
