Package Exports
- @thalorlabs/middleware
- @thalorlabs/middleware/dist/index.js
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (@thalorlabs/middleware) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
@thalorlabs/middleware
A comprehensive Express.js middleware collection for TypeScript applications with authentication and error handling capabilities.
Installation
npm install @thalorlabs/middleware
Features
- Authentication middleware with API key validation
- Error handling middleware with try-catch wrapper and structured error responses
- Protected access middleware combining authentication and error handling
- TypeScript support with full type safety
- Configurable options for different use cases
- Request tracking with requestId and context support
- Structured error responses for consistent API responses
Quick Start
import express from 'express';
import {
protect,
TryCatchMiddleware,
protectedAccessMiddleware,
} from '@thalorlabs/middleware';
const app = express();
// Basic authentication middleware
app.use('/api', TryCatchMiddleware(protect));
// Protected route with custom handler
app.get(
'/api/data',
protectedAccessMiddleware(async (req, res) => {
res.json({ message: 'Protected data' });
}, 'your-api-key')
);
// Custom middleware with error handling
app.get(
'/api/custom',
TryCatchMiddleware(async (req, res) => {
// Your custom logic here
res.json({ success: true });
})
);
Middleware Components
Authentication Middleware (protect
)
Validates API keys from request headers.
import { protect } from '@thalorlabs/middleware';
// Basic usage (no validation - for testing)
app.use('/api', TryCatchMiddleware(protect));
// With API key validation
app.use(
'/api',
TryCatchMiddleware(async (req, res) => {
await protect(req, res, 'your-secret-api-key');
})
);
// With multiple valid API keys
app.use(
'/api',
TryCatchMiddleware(async (req, res) => {
await protect(req, res, ['key1', 'key2', 'key3']);
})
);
Request Headers:
x-api-key
: The API key to validate
Response:
- Sets
res.locals.apiKey
with the validated API key - Throws
AuthenticationError
if validation fails
Error Handling Middleware (TryCatchMiddleware
)
Wraps middleware functions with comprehensive error handling.
import { TryCatchMiddleware } from '@thalorlabs/middleware';
// Wrap any async middleware function
const safeMiddleware = TryCatchMiddleware(async (req, res, next) => {
// Your middleware logic here
// Errors will be automatically caught and handled
});
app.use('/api', safeMiddleware);
Error Handling:
- CustomError instances: Returns structured error responses
- ZodError instances: Converts to ValidationError format
- MongoDB duplicate key errors: Converts to DatabaseError
- Generic errors: Returns appropriate HTTP status codes
Protected Access Middleware (protectedAccessMiddleware
)
Combines authentication and error handling for protected routes.
import { protectedAccessMiddleware } from '@thalorlabs/middleware';
// Single API key
app.get(
'/api/protected',
protectedAccessMiddleware(async (req, res) => {
res.json({ message: 'This is protected data' });
}, 'your-api-key')
);
// Multiple API keys
app.post(
'/api/admin',
protectedAccessMiddleware(
async (req, res) => {
res.json({ message: 'Admin operation completed' });
},
['admin-key-1', 'admin-key-2']
)
);
// No API key validation (for testing)
app.get(
'/api/test',
protectedAccessMiddleware(async (req, res) => {
res.json({ message: 'Test endpoint' });
})
);
Type Definitions
MiddlewareFunction
type MiddlewareFunction<T = Record<string, string>> = (
req: Request<T>,
res: Response,
next: NextFunction
) => Promise<void>;
Error Handling
The middleware integrates with @thalorlabs/errors
for consistent error responses:
CustomError Integration
import { BadRequestError, ValidationError } from '@thalorlabs/errors';
app.get(
'/api/example',
TryCatchMiddleware(async (req, res) => {
if (!req.query.id) {
throw new BadRequestError('ID parameter is required');
}
// Validation errors
if (someValidationFails) {
throw new ValidationError('Validation failed', [
{ field: 'email', message: 'Invalid email format' },
]);
}
res.json({ success: true });
})
);
Error Response Format
{
"status": "failure",
"error": "Error message",
"timestamp": "2024-01-01T12:00:00.000Z",
"requestId": "req-123",
"context": {
"additional": "context"
}
}
Advanced Usage
Custom Error Handling
import { TryCatchMiddleware } from '@thalorlabs/middleware';
const customErrorHandler = TryCatchMiddleware(async (req, res) => {
// Your logic that might throw errors
if (someCondition) {
throw new Error('Custom error message');
}
res.json({ success: true });
});
app.use('/api/custom', customErrorHandler);
Multiple Middleware Layers
import { protect, TryCatchMiddleware } from '@thalorlabs/middleware';
// Authentication + custom logic
app.get(
'/api/data',
TryCatchMiddleware(async (req, res) => {
await protect(req, res, 'api-key');
}),
TryCatchMiddleware(async (req, res) => {
// Additional middleware logic
res.json({ data: 'some data' });
})
);
Configuration
Environment Variables
# Optional: Set default API key
API_KEY=your-default-api-key
# Optional: Set environment
NODE_ENV=production
TypeScript Configuration
The package includes full TypeScript support. Make sure your tsconfig.json
includes:
{
"compilerOptions": {
"target": "ES2020",
"module": "CommonJS",
"declaration": true,
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true
}
}
Testing
# Run tests
npm test
# Run tests with coverage
npm run test:coverage
# Run tests in watch mode
npm run test:watch
Dependencies
@thalorlabs/errors
: Error handling classes@thalorlabs/types
: Type definitionsexpress
: Express.js frameworkuuid
: UUID generationzod
: Schema validation (for error handling)
Peer Dependencies
express
: ^4.18.0
Best Practices
1. Always Use TryCatchMiddleware
// Good
app.use('/api', TryCatchMiddleware(protect));
// Avoid
app.use('/api', protect); // Errors won't be handled properly
2. Use Protected Access for Routes
// Good - combines auth and error handling
app.get('/api/data', protectedAccessMiddleware(handler, 'api-key'));
// More verbose but equivalent
app.get(
'/api/data',
TryCatchMiddleware(async (req, res) => {
await protect(req, res, 'api-key');
await handler(req, res);
})
);
3. Provide API Keys
// Good - validates API keys
app.use(
'/api',
TryCatchMiddleware(async (req, res) => {
await protect(req, res, process.env.API_KEY);
})
);
// Avoid - no validation (only for testing)
app.use('/api', TryCatchMiddleware(protect));
4. Use Request IDs for Tracing
import { v4 as uuidv4 } from 'uuid';
app.use((req, res, next) => {
req.headers['x-request-id'] = req.headers['x-request-id'] || uuidv4();
next();
});
Migration Guide
From Basic Express Middleware
// Before
app.get('/api/data', async (req, res) => {
try {
// Validate API key
if (req.headers['x-api-key'] !== 'secret') {
return res.status(401).json({ error: 'Unauthorized' });
}
// Your logic
res.json({ data: 'some data' });
} catch (error) {
res.status(500).json({ error: 'Internal server error' });
}
});
// After
app.get(
'/api/data',
protectedAccessMiddleware(async (req, res) => {
res.json({ data: 'some data' });
}, 'secret')
);
License
ISC
This middleware collection provides a solid foundation for building robust Express.js applications with proper authentication and error handling.