Middleware Introduction

Every Koa Feature Is an async Function Registered With app.use

Middleware Introduction

Koa middleware are async functions that receive ctx and next. Learn how to write them, register them with app.use, and understand the execution stack.

3 min read Level 1/5 #koa#middleware#app.use
What you'll learn
  • Write a Koa middleware function with the correct async (ctx, next) signature
  • Register middleware with app.use and understand registration order
  • Know when to call await next() and when to skip it

In Koa, everything is middleware. There is no built-in routing layer, no automatic body parsing, and no bundled session support. Each capability is a function you register with app.use, and Koa calls them in order for every incoming request.

The Middleware Signature

A Koa middleware function is an async function that accepts two arguments:

async function myMiddleware(ctx, next) {
  // 1. Code here runs before the rest of the stack
  await next();
  // 2. Code here runs after the rest of the stack has finished
}

app.use(myMiddleware);
  • ctx — the context object (request + response + helpers).
  • next — a function that, when awaited, passes control to the next middleware in the stack.

Registering Middleware

app.use() appends functions to the middleware stack in the order you call it:

import Koa from "koa";

const app = new Koa();

app.use(async (ctx, next) => {
  console.log("Middleware A — before");
  await next();
  console.log("Middleware A — after");
});

app.use(async (ctx, next) => {
  console.log("Middleware B — before");
  await next();
  console.log("Middleware B — after");
});

app.use(async (ctx) => {
  ctx.body = "Hello!";
  console.log("Middleware C — response set");
});

app.listen(3000);

For a single request, the console output is:

Middleware A — before
Middleware B — before
Middleware C — response set
Middleware B — after
Middleware A — after

Skipping next()

If you do not call await next(), the middleware stack stops there. This is useful for short-circuiting a request, such as returning a cached response or rejecting an unauthenticated request:

app.use(async (ctx, next) => {
  if (!ctx.headers["x-api-key"]) {
    ctx.status = 401;
    ctx.body = { error: "Unauthorized" };
    return; // do not call next()
  }
  await next();
});

Up Next

See how calling await next() creates Koa’s signature “onion model” of cascading middleware.

Cascading Middleware →