← All tasksNode.js · Middle
A Koa-style middleware chain
Write compose(middlewares): it returns a function (ctx) => Promise that runs the middlewares in order. Each one gets ctx and next; await next() hands control to the next one and waits until the rest of the chain finishes, and the code after it runs on the way back (the "onion").
If a middleware doesn't call next(), the rest doesn't run. An error in any middleware (sync or async) rejects the promise and can be caught with try/catch around await next() higher up the chain. Calling next() twice in one middleware is an error: next() called multiple times.
Ctrl+Enter runs the tests. Tab indents; to leave the editor with the keyboard, press Esc and then Tab. Your code is saved in this browser.
Tests (6)
const log = []; const app = compose([ async (ctx, next) => { log.push('a1'); await next(); log.push('a2'); }, async (ctx, next) => { log.push('b1'); await next(); log.push('b2'); }, async () => { log.push('c'); }, ]); await app({}); expect(log).toEqual(['a1', 'b1', 'c', 'b2', 'a2']);
const app = compose([ async (ctx, next) => { ctx.user = 'ann'; await next(); }, async (ctx) => { ctx.body = 'hello ' + ctx.user; }, ]); const ctx = {}; await app(ctx); expect(ctx.body).toBe('hello ann');
const third = fn(); const app = compose([ async (ctx, next) => { ctx.status = 401; }, async () => third(), ]); const ctx = {}; await app(ctx); expect(ctx.status).toBe(401); expect(third).toHaveBeenCalledTimes(0);
const app = compose([ async (ctx, next) => { try { await next(); } catch (e) { ctx.status = 500; ctx.error = e.message; } }, async () => { throw new Error('db down'); }, ]); const ctx = {}; await app(ctx); expect(ctx).toEqual({ status: 500, error: 'db down' });
const app = compose([async (ctx, next) => { await next(); await next(); }]); await expect(app({})).rejects.toThrow('next() called multiple times');
const app = compose([(ctx, next) => { throw new Error('sync boom'); }]); await expect(app({})).rejects.toThrow('sync boom'); await compose([])({});