How is koa middleware different from express middleware?

Viewed 2381

Except for the obvious, async/await and the middleware signatures, koa's middleware app.use(async (ctx, next) => {...}) vs express app.use(function (req, res, next) {...})

How would koa's middleware make a difference ? Why would that be called onion model ?

------ update ------

My original question probably wasn't clearly enough. I was wondering why koa middleware is better than express middleware ?

------ update 2 ------

As Mastering Koa Middleware explained the error handling is indeed better in koa.

2 Answers

Well, in koajs, there is the option of cascading the middlewares:

An example from the koa page states this:

// logger

app.use(async (ctx, next) => {
  await next();
  const rt = ctx.response.get('X-Response-Time');
  console.log(`${ctx.method} ${ctx.url} - ${rt}`);
});

// x-response-time

app.use(async (ctx, next) => {
  const start = Date.now();
  await next();
  const ms = Date.now() - start;
  ctx.set('X-Response-Time', `${ms}ms`);
});

// response

app.use(async ctx => {
  ctx.body = 'Hello World';
});

Compared to express, the flow of the above code will be:

  1. Logger middleware will pause execution of its own code, and move on to x-response-time middleware,

  2. x-response-time will stat a time, then pause its code execution and hand control over to the next middleware (response),

  3. The next middleware is response, it will set the response in the body, then unwind code execution back to x-response-time

  4. x-response-time will continue its code execution from where it left off and calculate the time it took to set the response, then unwind code execution back to the logger middleware

  5. Logger middleware will log the response time.

Given the above example, you could use this to your advantage. The first middleware (logger) can be swapped with one that catches exceptions, such that it will wrap all the following middlewares in a try catch block

Express next is called at the end of middleware call and gives the control to next middleware. Previous middleware is unaware of how next middleware is executed. Middlewares can interact in one way, the last middleware is usually responsible for sending a request.

As Koa 2 documentation explains, next() returns the result of next middleware in the stack, i.e. a promise:

app.use(async (ctx, next) => {
  const start = Date.now();
  await next();
  const ms = Date.now() - start;
  console.log(`${ctx.method} ${ctx.url} - ${ms}ms`);
});

Koa middlewares can interact in two ways, it's unnecessary that the last middleware in the stack sends a response, response assignment can be delegated to previous middleware. Since the response isn't sent explicitly like res.send() but after middleware stack execution is finished, it's possible to modify it in other middlewares (this can be used for good or bad):

app.use(function thirdPartyMiddlewareWeCannotChange(ctx, next) {
  if (!auth)
    ctx.redirect('/login');

  next();
});

app.use(router.routes());

router.get('/no-auth-here', (ctx) => {
  ctx.status = 200;
  ctx.body = 'hi';
});
Related