Problem: Factory for Dynamically Composed Middleware Chains

Hard
40 min
Create a factory that returns a composed middleware function based on a list of configured behaviors.

Problem statement

You’re designing a middleware engine where different middleware behaviors (e.g., logging, auth, rate limiting) can be turned on or off using a config array like:

const config = ['logger', 'auth'];

Each middleware is a class with a .handle(req, next) method that must call next() to pass control. You need to generate a composed function that runs the middlewares in order, applying .handle() on each. Each one must wrap the next like a chain of responsibility—a pattern where each handler passes control to the next in a linked sequence.

We’ll cover Chain of Responsibility Pattern separately, but for now, think of it as a way to build modular pipelines where each step decides whether to continue.

Goal

Implement createMiddlewarePipeline(config) that returns a composed middleware function like:

const fn = createMiddlewarePipeline(['logger', 'auth']);
await fn(req);

Constraints

  • No hardcoded branching logic.

  • The middleware classes are predefined and should not be edited.

  • The order of execution must match the order in the config array.

  • The factory must return a single function that handles the full chain.

Sample output

The examples below illustrate what the output should look like:

// Example usage
async function runPipelines() {
// Pipeline 1 – Single middleware
const pipeline1 = createMiddlewarePipeline(['logger']);
await pipeline1({});
console.log('--- End of pipeline 1 ---');
// Pipeline 2 – Two middlewares
const pipeline2 = createMiddlewarePipeline(['auth', 'logger']);
await pipeline2({});
console.log('--- End of pipeline 2 ---');
// Pipeline 3 – All middlewares in one order
const pipeline3 = createMiddlewarePipeline(['rateLimit', 'logger', 'auth']);
await pipeline3({});
console.log('--- End of pipeline 3 ---');
// Pipeline 4 – All middlewares in another order
const pipeline4 = createMiddlewarePipeline(['auth', 'rateLimit', 'logger']);
await pipeline4({});
console.log('--- End of pipeline 4 ---');
// Pipeline 5 – No middleware
const pipeline5 = createMiddlewarePipeline([]);
await pipeline5({});
console.log('--- End of pipeline 5 ---');
}
runPipelines();
/* Expected output:
Logger start
Logger end
--- End of pipeline 1 ---
Auth start
Logger start
Logger end
Auth end
--- End of pipeline 2 ---
RateLimit start
Logger start
Auth start
Auth end
Logger end
RateLimit end
--- End of pipeline 3 ---
Auth start
RateLimit start
Logger start
Logger end
RateLimit end
Auth end
--- End of pipeline 4 ---
--- End of pipeline 5 --- */

Good luck trying the problem! If you’re unsure how to proceed, check the “Solution” tab above.

Problem: Factory for Dynamically Composed Middleware Chains

Hard
40 min
Create a factory that returns a composed middleware function based on a list of configured behaviors.

Problem statement

You’re designing a middleware engine where different middleware behaviors (e.g., logging, auth, rate limiting) can be turned on or off using a config array like:

const config = ['logger', 'auth'];

Each middleware is a class with a .handle(req, next) method that must call next() to pass control. You need to generate a composed function that runs the middlewares in order, applying .handle() on each. Each one must wrap the next like a chain of responsibility—a pattern where each handler passes control to the next in a linked sequence.

We’ll cover Chain of Responsibility Pattern separately, but for now, think of it as a way to build modular pipelines where each step decides whether to continue.

Goal

Implement createMiddlewarePipeline(config) that returns a composed middleware function like:

const fn = createMiddlewarePipeline(['logger', 'auth']);
await fn(req);

Constraints

  • No hardcoded branching logic.

  • The middleware classes are predefined and should not be edited.

  • The order of execution must match the order in the config array.

  • The factory must return a single function that handles the full chain.

Sample output

The examples below illustrate what the output should look like:

// Example usage
async function runPipelines() {
// Pipeline 1 – Single middleware
const pipeline1 = createMiddlewarePipeline(['logger']);
await pipeline1({});
console.log('--- End of pipeline 1 ---');
// Pipeline 2 – Two middlewares
const pipeline2 = createMiddlewarePipeline(['auth', 'logger']);
await pipeline2({});
console.log('--- End of pipeline 2 ---');
// Pipeline 3 – All middlewares in one order
const pipeline3 = createMiddlewarePipeline(['rateLimit', 'logger', 'auth']);
await pipeline3({});
console.log('--- End of pipeline 3 ---');
// Pipeline 4 – All middlewares in another order
const pipeline4 = createMiddlewarePipeline(['auth', 'rateLimit', 'logger']);
await pipeline4({});
console.log('--- End of pipeline 4 ---');
// Pipeline 5 – No middleware
const pipeline5 = createMiddlewarePipeline([]);
await pipeline5({});
console.log('--- End of pipeline 5 ---');
}
runPipelines();
/* Expected output:
Logger start
Logger end
--- End of pipeline 1 ---
Auth start
Logger start
Logger end
Auth end
--- End of pipeline 2 ---
RateLimit start
Logger start
Auth start
Auth end
Logger end
RateLimit end
--- End of pipeline 3 ---
Auth start
RateLimit start
Logger start
Logger end
RateLimit end
Auth end
--- End of pipeline 4 ---
--- End of pipeline 5 --- */

Good luck trying the problem! If you’re unsure how to proceed, check the “Solution” tab above.

Node.js
class LoggerMiddleware {
async handle(req, next) {
console.log('Logger start');
req.log = '[logged]';
await next();
console.log('Logger end');
}
}
class AuthMiddleware {
async handle(req, next) {
console.log('Auth start');
req.user = { name: 'admin' };
await next();
console.log('Auth end');
}
}
class RateLimitMiddleware {
async handle(req, next) {
console.log('RateLimit start');
req.limited = true;
await next();
console.log('RateLimit end');
}
}
const middlewareMap = {
logger: LoggerMiddleware,
auth: AuthMiddleware,
rateLimit: RateLimitMiddleware
};
// Implement this
function createMiddlewarePipeline(config) {
// Return a function that executes all configured middlewares in order
}
// Example usage
async function runPipelines() {
// Pipeline 1 – Single middleware
const pipeline1 = createMiddlewarePipeline(['logger']);
await pipeline1({});
console.log('--- End of pipeline 1 ---');
// Pipeline 2 – Two middlewares
const pipeline2 = createMiddlewarePipeline(['auth', 'logger']);
await pipeline2({});
console.log('--- End of pipeline 2 ---');
// Pipeline 3 – All middlewares in one order
const pipeline3 = createMiddlewarePipeline(['rateLimit', 'logger', 'auth']);
await pipeline3({});
console.log('--- End of pipeline 3 ---');
// Pipeline 4 – All middlewares in another order
const pipeline4 = createMiddlewarePipeline(['auth', 'rateLimit', 'logger']);
await pipeline4({});
console.log('--- End of pipeline 4 ---');
// Pipeline 5 – No middleware
const pipeline5 = createMiddlewarePipeline([]);
await pipeline5({});
console.log('--- End of pipeline 5 ---');
}
runPipelines();
/* Expected output:
Logger start
Logger end
--- End of pipeline 1 ---
Auth start
Logger start
Logger end
Auth end
--- End of pipeline 2 ---
RateLimit start
Logger start
Auth start
Auth end
Logger end
RateLimit end
--- End of pipeline 3 ---
Auth start
RateLimit start
Logger start
Logger end
RateLimit end
Auth end
--- End of pipeline 4 ---
--- End of pipeline 5 --- */