AniUI Academy

Run a middleware chain with next()

hardFunctions & Closures+350 XPPractice

A middleware chain is the most useful closure pattern there is. Each function receives a shared context and a next callback; whatever it does before calling next() happens on the way in, and whatever it does after happens on the way out, once everything downstream has finished. Not calling next() short-circuits the rest of the chain — which is how an auth middleware rejects a request.

Implement createPipeline(). It returns an object with use(middleware), which registers a middleware and returns the pipeline itself so registrations can be chained, and run(context), which executes the middlewares synchronously in registration order and returns the same context object it was given. Each middleware is called as middleware(context, next). Calling next() runs the rest of the chain and returns once the rest of the chain has finished, so code after next() runs in reverse registration order. A middleware that never calls next() prevents every later middleware from running. Calling next() more than once in the same middleware throws new Error("next() called multiple times"). A pipeline with no middlewares simply returns the context.

Returns

{ use(middleware): this; run(context): any } — A pipeline whose use chains registrations and whose run dispatches the chain over a context.

Examples

const pipeline = createPipeline()
  .use((ctx, next) => { ctx.steps.push("a-in"); next(); ctx.steps.push("a-out"); })
  .use((ctx, next) => { ctx.steps.push("b-in"); next(); ctx.steps.push("b-out"); });

pipeline.run({ steps: [] });
// { steps: ["a-in", "b-in", "b-out", "a-out"] } — onion order

Code before next() runs on the way in; code after runs on the way out.

const pipeline = createPipeline()
  .use((ctx, next) => { if (!ctx.user) return; next(); })  // guard
  .use((ctx, next) => { ctx.handled = true; next(); });

pipeline.run({});           // { } — guard skipped the handler
pipeline.run({ user: 1 });  // { user: 1, handled: true }

What it has to do

  • use registers a middleware and returns the pipeline so calls chain.
  • run executes middlewares in registration order and returns the context it was given.
  • Code after next() runs on the way back out, in reverse registration order.
  • A middleware that does not call next() stops the rest of the chain.
  • Calling next() twice throws Error("next() called multiple times").

Notes

  • Recursion, not a loop: a dispatch(index) function that gives each middleware a next closing over index + 1. The call stack is the "onion".
  • Track the highest-dispatched index to detect a second next() — a duplicate dispatch would otherwise silently re-run the tail of the chain.
Asked atStripeShopifyMeta

Stuck? The javascript course covers everything this challenge needs.

Try it yourself
Loading playground...
5 tests · +350 XP

Discussion

Sign in to post
Sign in to join the conversation.Sign in

No comments yet. Be the first to say something.