Skip to content
Back to skills

Koa

BSecurity

Koa is a minimalist Node.js web framework from the team behind Express, built around async/await middleware and a single context object. Use when a user asks to build a REST API or web service with Koa, add routing, body parsing, CORS or error handling to a Koa app, write custom middleware, or migrate from Express or from Koa 2 to Koa 3.

  • 142 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added May 29, 2026
developmentjavascripttypescriptrustgojavabashnodeexpressgitapi

Works with

  • terminal
  • cli
  • api

Security analysis

B88/100
  • mediumUses curl or wget to download content
  • mediumInstalls packages at runtime which could introduce malicious dependencies

Pro scans all 2 files and shows the line behind each finding

Scanned October 4, 2026

npx -y skills add TerminalSkills/skills --skill koa --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Koa?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Koa
[![Security: B — Skills Directory](https://www.skillsdirectory.com/api/skills/terminalskills-koa/badge)](https://www.skillsdirectory.com/skills/terminalskills-koa)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: koa
description: >-
  Koa is a minimalist Node.js web framework from the team behind Express,
  built around async/await middleware and a single context object. Use when
  a user asks to build a REST API or web service with Koa, add routing, body
  parsing, CORS or error handling to a Koa app, write custom middleware, or
  migrate from Express or from Koa 2 to Koa 3.
license: Apache-2.0
compatibility: 'Node.js 18+ (Koa 3); @koa/router 15 needs Node.js 20+'
metadata:
  author: terminal-skills
  version: 1.1.0
  category: development
  repository: https://github.com/koajs/koa
  tags:
    - node
    - web-framework
    - middleware
    - async
    - express-alternative
---

# Koa — Next-Generation Node.js Web Framework

## Overview

Koa (current major version 3.x) is a small framework: it ships the application object, the `ctx` context (request plus response helpers) and the middleware pipeline, and nothing else. Routing, body parsing, CORS and logging are separate packages you add. Middleware are async functions that call `await next()`; code before the call runs on the way down, code after it runs on the way back up (the "onion" model).

Koa 3 requires Node.js 18 or newer, no longer accepts generator-function middleware (`koa-convert` is gone), and builds `ctx.query` with `URLSearchParams`. `@koa/router` 15 requires Node.js 20 or newer.

## Instructions

### Install

```bash
npm install koa @koa/router @koa/bodyparser @koa/cors koa-logger
npm install -D @types/koa @types/koa-logger @types/koa__cors   # TypeScript only
```

`@koa/router` ships its own types, so do not install `@types/koa__router`. Koa itself has no bundled types: add `@types/koa`. `koa-compose` comes in as a dependency of Koa; install it explicitly if you import it directly.

### Application skeleton

```javascript
import Koa from "koa";
import Router from "@koa/router";
import { bodyParser } from "@koa/bodyparser";   // named export, not default
import cors from "@koa/cors";
import logger from "koa-logger";

const app = new Koa();
const router = new Router({ prefix: "/api" });

// 1. Error handler goes first so it wraps everything below it
app.use(async (ctx, next) => {
  try {
    await next();
  } catch (err) {
    ctx.status = err.status || 500;
    ctx.body = { error: err.expose ? err.message : "Internal Server Error" };
    ctx.app.emit("error", err, ctx);          // still reaches app.on("error")
  }
});

app.use(logger());
app.use(cors());
app.use(bodyParser());                         // JSON and urlencoded by default

router.post("/orders", async (ctx) => {
  const { customerId, items } = ctx.request.body;
  if (!customerId || !Array.isArray(items)) {
    ctx.throw(400, "customerId and items are required");
  }
  ctx.status = 201;
  ctx.body = { id: "ord_1042", customerId, itemCount: items.length };
});

router.get("/orders/:id", async (ctx) => {
  ctx.assert(ctx.params.id.startsWith("ord_"), 404, "Order not found");
  ctx.body = { id: ctx.params.id };
});

app.use(router.routes()).use(router.allowedMethods());   // 405/OPTIONS handling

app.on("error", (err) => console.error("unhandled", err));
app.listen(3000, () => console.log("listening on :3000"));
```

### Context essentials

- `ctx.request.body` — parsed body (set by the body parser; `{}` when nothing was parsed). Parsing only runs for POST, PUT and PATCH.
- `ctx.body = value` — object or array becomes JSON, string becomes text/HTML, a stream is piped. Setting a body on a status-less response sets 200.
- `ctx.status`, `ctx.set(name, value)`, `ctx.get(name)`, `ctx.query`, `ctx.params` (router), `ctx.state` (per-request scratch space shared by middleware).
- `ctx.throw(status, message)` creates an HTTP error; with 4xx statuses the message is exposed to the client (`err.expose`), with 5xx it is not. `ctx.assert(condition, status, message)` is the one-line form.
- Koa 3 changed the signature: `ctx.throw(status, error, properties)` — pass an `Error` rather than relying on string-only forms.
- `ctx.back()` replaces `ctx.redirect("back")` in Koa 3.
- Set `app.proxy = true` only behind a trusted reverse proxy, so `ctx.ip` and `ctx.protocol` honour `X-Forwarded-*`.

### Routing with @koa/router 15

- Paths use path-to-regexp 8 syntax. Regex inside parameters (`/:id(\\d+)`) no longer works and throws at startup; validate in middleware, `router.param()` or `createParameterValidationMiddleware`.
- Optional segment: `/users{/:id}`. Wildcard capture: `/files/*path` gives `ctx.params.path`.
- Nest routers: `apiRouter.use("/users", usersRouter.routes())`.
- `router.use(mw)` registers middleware for the routes of that router only.

### Middleware composition

```javascript
import compose from "koa-compose";

const requireAdmin = compose([requireAuth, requireRole("admin"), auditLog]);
router.delete("/users/:id", requireAdmin, deleteUser);

async function requireAuth(ctx, next) {
  const token = ctx.get("Authorization").replace(/^Bearer /, "");
  if (!token) ctx.throw(401, "Missing bearer token");
  ctx.state.user = await verifyToken(token);   // your own verification
  await next();
}
```

### Multipart uploads and other body types

`@koa/bodyparser` does not parse multipart. Use `@koa/multer` (or `busboy`) for file uploads. Other types are opt-in: `bodyParser({ enableTypes: ["json", "form", "text"], jsonLimit: "2mb" })`. Defaults: `jsonLimit` and `textLimit` 1mb, `formLimit` 56kb.

## Examples

### Example 1: Add a health endpoint and response-time header

User request: "Add a /health route to my Koa app and a header with how long each request took."

```javascript
app.use(async (ctx, next) => {
  const start = process.hrtime.bigint();
  await next();                                  // everything downstream runs here
  const ms = Number(process.hrtime.bigint() - start) / 1e6;
  ctx.set("X-Response-Time", `${ms.toFixed(1)}ms`);
});

router.get("/health", (ctx) => {
  ctx.body = { status: "ok", uptimeSeconds: Math.round(process.uptime()) };
});
```

Check it: `curl -i http://localhost:3000/api/health` returns `200`, a JSON body such as `{"status":"ok","uptimeSeconds":12}` and an `X-Response-Time: 0.4ms` header.

### Example 2: Migrate an Express handler

User request: "Convert this Express route to Koa: `app.post('/login', (req, res, next) => { ... res.status(401).json({error:'bad credentials'}) })`."

```javascript
router.post("/login", async (ctx) => {
  const { email, password } = ctx.request.body;
  const user = await users.findByEmail(email);
  if (!user || !(await verifyPassword(user, password))) {
    ctx.throw(401, "bad credentials");           // replaces res.status(401).json(...) and next(err)
  }
  ctx.body = { token: await issueToken(user) };
});
```

Result: the request returns `401 {"error":"bad credentials"}` through the error-handling middleware shown above; there is no `next(err)` call, errors propagate as rejected promises.

## Guidelines

- Register the error handler first; middleware registered above it are not protected by it.
- Always `await next()`. A forgotten `await` makes downstream work run after the response is sent.
- Do not set `ctx.body` after the response has been written; use `ctx.respond = false` only when you write to `ctx.res` yourself.
- Upgrading from Koa 2: rewrite generator middleware as async functions, check code that reads `ctx.query` arrays, and update `ctx.throw` and `redirect("back")` calls.
- Upgrading `@koa/router` to v13 or newer: remove `:param(regex)` routes and old `*` wildcards, and drop `@types/koa__router`.
- Prefer `@koa/bodyparser` and `@koa/cors` (the maintained scoped packages) in new code; `koa-bodyparser` is the older package.
- Koa has no built-in static file serving, validation or sessions: add `koa-static`, a schema validator or `koa-session` deliberately and pin versions.
- Choose another framework when you want batteries included (NestJS) or schema-driven validation and serialization built in (Fastify).

Files in this skill

  • SKILL.md4.2 KB
  • _scores.json1.5 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…