Documentation Index Fetch the complete documentation index at: https://mintlify.com/juanjh1/asimilation/llms.txt
Use this file to discover all available pages before exploring further.
Middleware functions execute before your route handlers, allowing you to add authentication, logging, validation, and other cross-cutting concerns to your application.
What is Middleware?
Middleware is a function that executes in the request/response cycle before your route handler. Middleware can:
Modify the request or response objects
Execute code before the route handler
Pass data to subsequent middleware or the route handler
Terminate the request early (e.g., for authentication failures)
Middleware Function Signature
Middleware functions have three parameters:
import { ArgumentedIncomingMessageAbc , ArgumentedServerResponseAbc } from '@asimilation/core' ;
function myMiddleware (
req : ArgumentedIncomingMessageAbc ,
res : ArgumentedServerResponseAbc ,
next : ( error ?: Error ) => void
) {
// Middleware logic here
// Call next() to pass control to the next middleware or route handler
next ();
}
Always call next() to continue the middleware pipeline. Forgetting to call next() will hang the request.
Adding Global Middleware
Global middleware runs on every request. Add it using MiddlewarePipeline:
import { MiddlewarePipeline } from '@asimilation/core' ;
MiddlewarePipeline . addMiddleware (( req , res , next ) => {
console . log ( ` ${ req . method } ${ req . url } ` );
next ();
});
Global middleware is managed by the MiddlewareManager singleton (see src/core/middleware-manager.ts:8-75).
Global Middleware Examples
Request Logging
CORS Headers
Request ID
Content-Type Parser
import { MiddlewarePipeline } from '@asimilation/core' ;
MiddlewarePipeline . addMiddleware (( req , res , next ) => {
const start = Date . now ();
console . log ( `[ ${ new Date (). toISOString () } ] ${ req . method } ${ req . url } ` );
// Continue to next middleware
next ();
// This runs after the response
const duration = Date . now () - start ;
console . log ( `Request completed in ${ duration } ms` );
});
Route-Specific Middleware
Apply middleware to specific routes using the handlers option:
import { url } from '@asimilation/core' ;
function authMiddleware ( req , res , next ) {
const token = req . headers . authorization ;
if ( ! token ) {
res . sendJson ({ error: 'Unauthorized' }, 401 );
return ; // Don't call next() - terminate here
}
// Validate token and attach user to request
( req as any ). user = { id: 1 , name: 'John' };
next ();
}
url . addPath ( '/admin/dashboard' , ( req , res ) => {
const user = ( req as any ). user ;
res . sendJson ({ message: `Welcome ${ user . name } ` }, 200 );
}, {
methods: [ 'GET' ],
handlers: [ authMiddleware ] // Route-specific middleware
});
Route-specific middleware is defined in the PathKwargs type (see src/core/type.ts:16-19).
Multiple Route Middlewares
Chain multiple middlewares for a single route:
import { url } from '@asimilation/core' ;
function authenticate ( req , res , next ) {
// Check authentication
const token = req . headers . authorization ;
if ( ! token ) {
res . sendJson ({ error: 'Unauthorized' }, 401 );
return ;
}
( req as any ). user = { id: 1 , role: 'admin' };
next ();
}
function authorize ( req , res , next ) {
// Check authorization
const user = ( req as any ). user ;
if ( user . role !== 'admin' ) {
res . sendJson ({ error: 'Forbidden' }, 403 );
return ;
}
next ();
}
function validateInput ( req , res , next ) {
// Validate request body
const body = ( req as any ). body ;
if ( ! body || ! body . name ) {
res . sendJson ({ error: 'Name is required' }, 400 );
return ;
}
next ();
}
url . addPath ( '/admin/users' , ( req , res ) => {
res . sendJson ({ message: 'User created' }, 201 );
}, {
methods: [ 'POST' ],
handlers: [ authenticate , authorize , validateInput ]
});
Middleware Execution Order
Middleware executes in a specific order:
Global middleware
All global middleware added via MiddlewarePipeline.addMiddleware() runs first, in the order they were added.
Route-specific middleware
Route-specific middleware from the handlers array runs next, in array order.
Route handler
Finally, your route handler function executes.
import { MiddlewarePipeline , url } from '@asimilation/core' ;
// 1. Global middleware (runs first)
MiddlewarePipeline . addMiddleware (( req , res , next ) => {
console . log ( 'Global middleware 1' );
next ();
});
MiddlewarePipeline . addMiddleware (( req , res , next ) => {
console . log ( 'Global middleware 2' );
next ();
});
// 2. Route with specific middleware
url . addPath ( '/test' , ( req , res ) => {
console . log ( 'Route handler' );
res . sendJson ({ message: 'Done' }, 200 );
}, {
handlers: [
( req , res , next ) => {
console . log ( 'Route middleware 1' );
next ();
},
( req , res , next ) => {
console . log ( 'Route middleware 2' );
next ();
}
]
});
// Output for GET /test:
// Global middleware 1
// Global middleware 2
// Route middleware 1
// Route middleware 2
// Route handler
The middleware execution order is managed by the router (see src/core/router-manager.ts:122-145).
Async Middleware
Middleware can be asynchronous:
import { MiddlewarePipeline } from '@asimilation/core' ;
import { MiddlewareFunctionAsync } from 'asimilation/types' ;
const asyncMiddleware : MiddlewareFunctionAsync = async ( req , res , next ) => {
try {
// Async operation (e.g., database query)
const user = await fetchUserFromDatabase ();
( req as any ). user = user ;
next ();
} catch ( error ) {
res . sendJson ({ error: 'Database error' }, 500 );
}
};
MiddlewarePipeline . addMiddleware ( asyncMiddleware );
Both MiddlewareFunction and MiddlewareFunctionAsync are supported (see src/core/type.ts:8-10).
Passing Data Between Middleware
Middleware can attach data to the request object for later use:
import { url } from '@asimilation/core' ;
function attachTimestamp ( req , res , next ) {
( req as any ). timestamp = Date . now ();
next ();
}
function attachUser ( req , res , next ) {
( req as any ). user = { id: 1 , name: 'Alice' };
next ();
}
url . addPath ( '/profile' , ( req , res ) => {
const timestamp = ( req as any ). timestamp ;
const user = ( req as any ). user ;
res . sendJson ({
user ,
requestTime: new Date ( timestamp ). toISOString ()
}, 200 );
}, {
handlers: [ attachTimestamp , attachUser ]
});
For TypeScript, consider extending the request interface to add type safety for custom properties.
Terminating the Request Early
Middleware can end the request without calling the route handler:
function rateLimiter ( req , res , next ) {
const ip = req . socket . remoteAddress ;
const requestCount = getRateLimitCount ( ip );
if ( requestCount > 100 ) {
// Terminate here - don't call next()
res . sendJson ({ error: 'Rate limit exceeded' }, 429 );
return ;
}
next (); // Continue to next middleware/handler
}
Error Handling in Middleware
Handle errors gracefully in middleware:
import { MiddlewarePipeline } from '@asimilation/core' ;
// Built-in error handling middleware
MiddlewarePipeline . addMiddleware (( req , res , next ) => {
try {
next ();
} catch ( error ) {
console . error ( error );
res . sendJson ({ message: 'Internal Server error' }, 500 );
}
});
Asimilation includes a default error-handling middleware (see src/default/middleware/error-handling.ts:5-18).
Common Middleware Patterns
Authentication
Role-Based Access
Request Validation
Caching
function requireAuth ( req , res , next ) {
const token = req . headers . authorization ?. replace ( 'Bearer ' , '' );
if ( ! token ) {
res . sendJson ({ error: 'No token provided' }, 401 );
return ;
}
try {
const decoded = verifyJWT ( token );
( req as any ). user = decoded ;
next ();
} catch ( error ) {
res . sendJson ({ error: 'Invalid token' }, 401 );
}
}
How Middleware Works Internally
Asimilation uses a dispatcher pattern to run middleware:
Middleware functions are stored in an array
A recursive dispatcher function calls each middleware in order
Each middleware must call next() to invoke the dispatcher with the next index
The pipeline continues until all middleware has executed
See the implementation in src/core/middleware-manager.ts:23-44.
Best Practices
Always call next()
Unless you’re terminating the request, always call next(): function myMiddleware ( req , res , next ) {
// Do work...
next (); // Required!
}
Check response state
Before writing to the response, check if it’s already ended: if ( res . writableEnded ) {
return ; // Response already sent
}
res . sendJson ({ data: 'value' }, 200 );
Order matters
Add middleware in the correct order. Authentication should come before authorization: handlers : [ authenticate , authorize , validate ]
Keep middleware focused
Each middleware should have a single responsibility: // Good: Focused middleware
function logRequest ( req , res , next ) { /* ... */ }
function parseBody ( req , res , next ) { /* ... */ }
// Less ideal: Does too much
function doEverything ( req , res , next ) { /* ... */ }
What’s Next?
Error Handling Learn how to handle errors in your middleware and routes
API Reference Explore the complete middleware API documentation