Node.js Framework

Koa

Koa ist eine winzige async/await-Neuschreibung von Express durch dessen ursprüngliche Autoren. Ein Context-Objekt und eine Zwiebel aus middleware — alles andere ist ein Paket Ihrer Wahl.

intermediate14 min readUpdated 16. Sept. 2026
app.js
js
// app.js
import Koa from "koa";
import Router from "@koa/router";

const app = new Koa();
const router = new Router();

router.get("/users/:id", async (ctx) => {
  const user = await db.user.findById(ctx.params.id);
  if (!user) ctx.throw(404, "user not found");
  ctx.body = user;
});

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

app.use(router.routes()).use(router.allowedMethods());

app.listen(3000);
Veröffentlicht
2013
Erstellt von
Dem Express-Team
Läuft auf
Node.js
Stil
Minimal, async/await
Kernidee
Onion middleware
Routing
@koa/router
Sprache
JavaScript / TypeScript

Warum es wichtig ist

Warum Koa existiert

Ein Kern, der klein genug zum Lesen ist

Koa liefert fast nichts mit: keinen Router, keinen Body-Parser, keinen statischen Dateiserver. Sie fügen genau die middleware hinzu, die Sie benötigen, was die Abhängigkeiten minimal und den Kontrollfluss übersichtlich hält.

Die Zwiebel statt einer flachen Kette

Middleware sind async-Funktionen, die await next() aufrufen. Code vor dem Aufruf wird beim Hineingehen ausgeführt, Code danach beim Hinausgehen. So umschließen Timing, Logging und Transaktionen den gesamten Request.

Middleware, die Sie selbst wählen

Alles jenseits des Kerns ist ein Paket. Wählen Sie einen Router, einen Parser, einen Logger und einen Session-Store und setzen Sie diese zusammen, ohne dass ein Framework den Rest für Sie entscheidet.

Das Gesamtbild

Context, Onion, Async

Ein Objekt hält Request und Response, middleware umschließen einander und async/await steuert den Fluss.

Context

Vereinheitlichen

Ein einziges ctx-Objekt führt Request und Response zusammen. ctx.body, ctx.status, ctx.params und ctx.state ersetzen das Jonglieren mit req und res.

Onion

Komponieren

Jede middleware ist eine async (ctx, next) => {}-Funktion. Der Aufruf von await next() gibt die Kontrolle weiter; der nachfolgende Code wird ausgeführt, sobald alles downstream abgeschlossen ist.

Async

Fluss

Koa wurde von Anfang an für Promises gebaut, sodass Fehler gewöhnliche Exceptions sind, die zu einem Handler aufsteigen, anstatt stillschweigend verloren zu gehen.

Eine kurze Geschichte

Ein kleines Framework, das den Rest beeinflusste

  1. 2013

    Koa wird angekündigt

    TJ Holowaychuk und das Express-Team veröffentlichen ein winziges Framework, das auf Generator-Funktionen und der co-Library basiert.

    13
  2. 2014

    Das Context-Objekt festigt sich

    Das zusammengeführte ctx-Objekt und das Onion-Middleware-Modell werden zur Struktur, die Koa noch heute verwendet.

    14
  3. 2017

    Koa 2 und async/await

    Node 7.6 bringt natives async/await, und Koa 2 ersetzt Generatoren durch einfache Promises.

    17
  4. 2019

    @koa/router

    Das langjährige koa-router-Paket wird umbenannt und als @koa/router an die Community übergeben.

    19
  5. Heute

    Ruhig und einflussreich

    Koa behält eine kleine, stabile API bei, während neuere Frameworks seine async-middleware-Ideen übernehmen.

    Heute

Der vollständige Leitfaden

Koa: Alles was Sie wissen müssen

Was ist Koa?

Koa ist ein minimales Web-Framework für Node.js, das vom selben Team entwickelt wurde wie Express. Während Express ein riesiges middleware-Ökosystem und die vertraute req/res-API aufgebaut hat, startete Koa mit zwei Zielen neu: ein winziger Core und erstklassige async/await-Unterstützung. Es ist weniger ein Konkurrent zu Express als vielmehr ein bewusstes Überdenken desselben.

Der ursprüngliche Express-Autor, TJ Holowaychuk, entwickelte Koa, um die Dinge zu beheben, die sich in Express zu dieser Zeit unpraktisch anfühlten – verschachtelte Callbacks, Ad-hoc-Fehlerbehandlung und ein Core, der mehr Funktionen angesammelt hatte, als die Autoren eigentlich wollten. Koa 1 nutzte Generator-Funktionen. Koa 2 erschien, nachdem Node 7.6 native async/await eingeführt hatte, und das ist die Version, die Sie heute verwenden werden.

Wenn Express Ihnen die middleware-Kette lehrt, dann zeigt Ihnen Koa, was passiert, wenn diese Kette eine Anfrage umschließen darf, anstatt sie nur zu durchlaufen.

Das Context-Objekt

Koa fasst den Request und die Response in einem einzigen Objekt zusammen, dem sogenannten Context, das konventionell ctx genannt wird. Anstatt req zu lesen und in res zu schreiben, lesen und schreiben Sie Eigenschaften an einem einzigen Objekt.

app.use(async (ctx) => {
  ctx.status = 200;               // response status
  ctx.type = "application/json";  // response content type
  ctx.body = { ok: true };        // response body
});

Der Request und die Response sind dennoch vollständig verfügbar, wenn Sie diese benötigen:

  • ctx.request — die gekapselte eingehende Nachricht (ctx.request.body, ctx.request.header).
  • ctx.response — die gekapselte ausgehende Nachricht (ctx.response.status).
  • ctx.params, ctx.query und ctx.request.body — Routing und geparste Eingaben.
  • ctx.state — ein einfaches Objekt zur Übergabe von Werten zwischen middleware, wie zum Beispiel dem authentifizierten Benutzer.
  • ctx.throw(status, message) — löst einen HTTP-Fehler aus, den die Error-middleware abfangen kann.

ctx.state ist der idiomatische Ort, um gemeinsam genutzte Daten zu hinterlegen. Die Authentication-middleware setzt ctx.state.user, und spätere middleware oder die Route lesen diesen Wert aus, ohne das Request-Objekt selbst zu verunreinigen.

Die Zwiebel: Middleware, die umschließt

Eine Koa middleware ist eine einzige async Funktion mit der Signatur (ctx, next). Der Aufruf von await next() gibt die Kontrolle an die nächste Ebene weiter; alles, was Sie nach dieser Zeile schreiben, wird ausgeführt, sobald die inneren Ebenen abgeschlossen sind.

request  ──▶  mw1 before ──▶  mw2 before ──▶  route

response ◀──  mw1 after  ◀──  mw2 after  ◀────────┘

Dies ist das Zwiebel-Modell (Onion Model) – die zentrale Idee, die Koa so besonders macht. Eine flache Kette kann Code nur vor der Antwort ausführen; die Zwiebel kann Code auf beiden Seiten ausführen.

app.use(async (ctx, next) => {
  const start = Date.now();

  await next(); // everything downstream runs here

  ctx.set("X-Response-Time", `${Date.now() - start}ms`);
});

Diese eine middleware misst die Zeit der gesamten Anfrage, einschließlich jeder Route und jeder anderen darunter registrierten middleware. Dasselbe Muster wird für Logging, Datenbanktransaktionen und Cleanup verwendet.

Eine middleware kann die Kette auch unterbrechen (Short-Circuiting): Wenn sie ctx.body setzt und next() niemals aufruft, endet die Anfrage an dieser Stelle. Auf diese Weise kann eine Authentifizierung eine Anfrage ablehnen, bevor sie eine Route erreicht.

Routing mit @koa/router

Koa besitzt keinen integrierten Router im Core, daher wird das Routing über @koa/router bereitgestellt, ein von der Community gepflegtes Paket.

import Koa from "koa";
import Router from "@koa/router";

const app = new Koa();
const router = new Router();

router.get("/posts", async (ctx) => {
  ctx.body = await Post.find().limit(20);
});

router.get("/posts/:id", async (ctx) => {
  const post = await Post.findById(ctx.params.id);
  if (!post) ctx.throw(404, "post not found");
  ctx.body = post;
});

app.use(router.routes());
app.use(router.allowedMethods());

router.routes() mountet die passenden Handler, und router.allowedMethods() antwortet mit dem korrekten 405, wenn der Pfad existiert, aber die Methode nicht. Router können verschachtelt und mit Präfixen versehen werden, wodurch eine große API modular bleibt – ähnlich wie bei express.Router().

Fehlerbehandlung und app.on(“error”)

Da Koa middleware gewöhnliche async-Funktionen sind, sind Fehler gewöhnliche Exceptions. Fangen Sie diese an einer zentralen Stelle ab, indem Sie den downstream-Aufruf umschließen.

app.use(async (ctx, next) => {
  try {
    await next();
  } catch (err) {
    ctx.status = err.status ?? 500;
    ctx.body = { error: err.expose ? err.message : "internal_error" };
    ctx.app.emit("error", err, ctx);
  }
});

Innerhalb eines Handlers erstellt ctx.throw(404, "not found") einen Fehler mit einem status, einer Nachricht und einem expose-Flag, das kennzeichnet, ob der Fehler sicher für Clients angezeigt werden kann. Fehler ohne Status werden zu 500, und ihre Nachricht wird in der Antwort ausgeblendet.

Nicht jeder Fehler kann in eine Antwort umgewandelt werden. Wenn ein Fehler auftritt, nachdem die Header bereits gesendet wurden, löst Koa stattdessen ein error-Event an der App aus:

app.on("error", (err, ctx) => {
  console.error(`${ctx.method} ${ctx.url}`, err);
});

Abonnieren Sie dieses Event in jedem Fall, damit unerwartete Fehler immer protokolliert werden.

Warum Koa keinen integrierten Router oder Body Parser besitzt

Der Kern von Koa ist bewusst fast leer gehalten. Er stellt dir den Context, die middleware-Pipeline und das HTTP-Grundgerüst zur Verfügung; Routing, Body Parsing, Cookies, Sessions, statische Dateien und Security-Header befinden sich hingegen in separaten Paketen.

Dies ist ein bewusster Trade-off. Ein minimaler Kern ist einfach zu prüfen, hat wenige Abhängigkeiten und entwickelt sich langsam, sodass es für Koa selbst schwierig ist, deine App zu beeinträchtigen. Der Preis dafür ist, dass du die Komposition selbst übernimmst: Du entscheidest, welchen Body Parser du hinzufügst, wie er parst und wo er registriert wird.

import bodyParser from "koa-bodyparser";

app.use(bodyParser());
app.use(router.routes());
app.use(router.allowedMethods());

Die Reihenfolge ist hier genauso entscheidend wie in Express. Ein Parser, der nach dem Router registriert wird, steht den Routes nicht zur Verfügung.

Koa oder Express?

Beide Frameworks verfolgen die gleichen Ansätze bei Request und Response, daher ist die Entscheidung primär eine Frage der Philosophie.

Wähle Express, wenn Bekanntheit am wichtigsten ist: Es ist das am weitesten verbreitete Node-Framework, bringt ein integriertes Routing mit und verfügt über die größte Sammlung an middleware. Für Teams, die schnell vorankommen wollen, ist es die sicherste Standardwahl.

Wähle Koa, wenn du einen kleineren Core und das Onion-Modell bevorzugst. Die asynchrone Fehlerbehandlung ist sauberer, das Context-Objekt erspart viel Gefrickel mit req/res, und du zahlst nur für die middleware, die du tatsächlich hinzufügst. Der Kompromiss ist ein kleineres Ökosystem und ein höherer Konfigurationsaufwand deinerseits.

Eine Koa-App testen

Da eine Koa-App eine middleware-Pipeline ist, lässt sie sich problemlos mit supertest testen. Exportieren Sie die App und übergeben Sie app.callback() an den Request-Helper, damit kein Port geöffnet werden muss.

import request from "supertest";
import app from "../app.js";

test("GET /posts returns a list", async () => {
  const res = await request(app.callback()).get("/posts").expect(200);
  expect(Array.isArray(res.body)).toBe(true);
});

Wie bei Express sollten Sie die App-Definition von app.listen() trennen, damit Tests sie importieren können, ohne einen Server zu starten.

Best Practices

  • Platzieren Sie die Error-Handling middleware an erster Stelle, damit sie alle anderen Layer umschließt.
  • Verwenden Sie ctx.state für Request-Scoped Values, wie zum Beispiel den authentifizierten Benutzer.
  • Setzen Sie ctx.body und ctx.status, anstatt die raw response direkt zu manipulieren.
  • Registrieren Sie bodyParser vor dem Router, damit ctx.request.body befüllt wird.
  • Nutzen Sie ctx.throw für erwartete HTTP-Fehler und einen einzelnen Handler für alle anderen Fälle.
  • Abonnieren Sie immer app.on("error"), um Fehler zu loggen, die nach dem Senden der Header auftreten.
  • Exportieren Sie die App separat vom Server, damit die Tests schnell bleiben.

Häufige Fehler

  • next() zweimal innerhalb einer middleware aufrufen und dadurch den nachfolgenden Code erneut ausführen.
  • await vor next() vergessen, wodurch die “Rückweg”-Hälfte der Onion-Struktur übersprungen wird.
  • Den Router vor dem Body Parser registrieren und feststellen, dass ctx.request.body leer ist.
  • Davon ausgehen, dass Koa einen integrierten Router oder Body Parser besitzt, und das falsche Paket importieren.
  • Fehler verschlucken, ohne sie zu emittieren, sodass nichts in den Logs erscheint.
  • ctx.res direkt mutieren und damit die Status- und Header-Verarbeitung von Koa umgehen.

Wie geht es weiter?

Koa ist die sauberste Demonstration von async middleware in Node, und sein Onion-Modell findet sich in Frameworks über alle Sprachen hinweg wieder. Wenn Sie einen umfangreicheren Funktionsumfang und mehr Geschwindigkeit suchen, lesen Sie den Fastify-Guide. Wenn Sie das Framework bevorzugen, aus dem Koa hervorgegangen ist, schauen Sie sich Express noch einmal an. Um die darunterliegende HTTP-Schicht von ctx zu verstehen, beginnen Sie mit dem HTTP-Guide und halten Sie die Node.js basics griffbereit.

In der Praxis

Die Zwiebel im Code

Wechseln Sie zwischen den Tabs, um zu sehen, wie middleware, Routing und Fehler zusammenpassen.

middleware/timing.js
export async function timing(ctx, next) {
  const start = Date.now();

  // runs on the way in
  await next();

  // runs on the way out, after every downstream middleware
  const ms = Date.now() - start;
  ctx.set("X-Response-Time", `${ms}ms`);
}

Die Reihenfolge der Zwiebel

Middleware werden beim Hineingehen von oben nach unten und beim Hinausgehen von unten nach oben ausgeführt. Wenn der Logger zuletzt registriert wird, umschließt er nur das, was nach ihm kommt.

Bevorzugt
app.use(timing);          // wraps everything below
app.use(logger);
app.use(router.routes());
Vermeiden
app.use(router.routes());
app.use(timing);          // never runs for matched routes

Auf einen Request antworten

Setzen Sie ctx.body und lassen Sie Koa die Antwort schreiben. Der Zugriff auf die rohe Node-Response umgeht die Status-, Header- und Fehlerbehandlung von Koa.

Bevorzugt
ctx.status = 201;
ctx.body = post;
Vermeiden
ctx.res.statusCode = 201;
ctx.res.end(JSON.stringify(post));

Abwägungen

Ist Koa die richtige Basis für Ihre API?

Koa bietet Ihnen einen kleinen, eleganten Kern und überlässt den Rest Ihnen. Das ist sowohl eine Stärke als auch ein Preis.

Strengths

  • Ein Kern, den man im Kopf behalten kann

    Das Framework besteht aus wenigen hundert Zeilen. Sie können den Quellcode lesen und genau wissen, wie ein Request vom Socket zur Response fließt.

  • Die Zwiebel ist wirklich nützlich

    Jeden Request mit Timing, Logging oder einer Datenbanktransaktion zu umschließen, wird trivial, da der Code nach await next() beim Hinausgehen ausgeführt wird.

  • Saubere async-Fehlerbehandlung

    Da middleware async-Funktionen sind, wird ein geworfener Fehler vom nächsten try/catch abgefangen, anstatt in einer unhandled rejection zu verschwinden.

Trade-offs

  • Sie setzen den Stack selbst zusammen

    Routing, Body-Parsing, Cookies und statische Dateien sind alles separate Pakete. Planen Sie Zeit für die Auswahl, die Verkabelung und die Kompatibilität ein.

  • Ein kleineres Ökosystem

    Es gibt weniger Koa-spezifische middleware als Express-middleware, obwohl die meisten Express-Pakete einen dünnen Koa-Wrapper oder ein direktes Äquivalent haben.

  • Weniger Anleitung für große Apps

    Koa hat keine Meinung zur Struktur. Teams müssen sich früh auf Konventionen einigen, sonst driften große Projekte in inkonsistente Handler ab.

Häufig gestellte Fragen

Häufig gestellte Fragen

Keep learning

Related topics from the roadmap.

$ Lernen Sie jetzt

Bereit, Koa zu lernen?

Unser interaktives Tutorial führt Sie Schritt für Schritt durch Koa — mit Quizzen und echtem Code, den Sie im Browser ausführen können.