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

bashtech-express-ts-middleware

v1.2.9

Published

Middleware for BashTech's TS-Node API

Readme

bashtech-express-ts-middleware

npm version TypeScript License: MIT

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

  • 🔐 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-middleware

or 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.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/AmazingFeature)
  3. Commit your changes (git commit -m 'Add some AmazingFeature')
  4. Push to the branch (git push origin feature/AmazingFeature)
  5. 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)