Node.js Framework

Koa

Koa é a reescrita minimalista em async/await do Express, feita por seus autores originais. Um objeto de contexto e uma cebola de middleware — todo o resto é um pacote que você escolhe.

intermediate14 min readUpdated 16 de set. de 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);
Lançado
2013
Criado por
Equipe do Express
Roda em
Node.js
Estilo
Minimalista, async/await
Ideia central
Onion middleware
Roteamento
@koa/router
Linguagem
JavaScript / TypeScript

Por que importa

Por que o Koa existe

Um núcleo pequeno o suficiente para ler

O Koa quase não traz nada: sem roteador, sem body parser, sem servidor de arquivos estáticos. Você adiciona exatamente o middleware que precisa, o que mantém a superfície de dependências minúscula e o fluxo de controle visível.

A cebola, não uma cadeia plana

Middlewares são funções assíncronas que chamam await next(). O código antes da chamada executa na entrada, e o código depois dela executa na saída; assim, temporização, logs e transações envolvem a requisição inteira.

Middleware que você realmente escolhe

Tudo além do núcleo é um pacote. Escolha um roteador, um parser, um logger e um armazenamento de sessão, e então componha-os sem que um framework decida o resto por você.

O panorama completo

Contexto, cebola, async

Um único objeto detém a requisição e a resposta, os middleware envolvem uns aos outros e o async/await conduz o fluxo.

Contexto

Unificar

Um único objeto ctx funde a requisição e a resposta. ctx.body, ctx.status, ctx.params e ctx.state substituem a malabarismo entre req e res.

Cebola

Compor

Cada middleware é (ctx, next) => {}. Chamar await next() passa o controle para a próxima camada; o código após isso executa assim que tudo abaixo termina.

Async

Fluxo

O Koa foi construído para promises desde o início, então erros são exceções comuns que sobem para um único manipulador em vez de serem descartados silenciosamente.

Uma breve historia

Um framework pequeno que influenciou os demais

  1. 2013

    Koa é anunciado

    TJ Holowaychuk e a equipe do Express lançam um framework minúsculo construído sobre generator functions e a biblioteca co.

    13
  2. 2014

    O objeto de contexto se consolida

    O objeto ctx fundido e o modelo de middleware em cebola tornam-se a estrutura que o Koa ainda usa hoje.

    14
  3. 2017

    Koa 2 e async/await

    O Node 7.6 traz async/await nativo, e o Koa 2 abandona generators em favor de promises puras.

    17
  4. 2019

    @koa/router

    O pacote koa-router, de longa data, é renomeado e entregue à comunidade como @koa/router.

    19
  5. Hoje

    Silencioso e influente

    O Koa mantém uma API pequena e estável enquanto frameworks mais novos pegam emprestadas suas ideias de middleware assíncrono.

    Hoje

O guia completo

Koa: Tudo que voce precisa saber

O que é Koa?

Koa é um framework web minimalista para Node.js escrito pela mesma equipe por trás do Express. Enquanto o Express desenvolveu um vasto ecossistema de middleware e a familiar API req/res, o Koa começou do zero com dois objetivos: um núcleo minúsculo e suporte de primeira classe a async/await. Ele é menos um concorrente do Express e mais uma reformulação deliberada dele.

O autor original do Express, TJ Holowaychuk, criou o Koa para corrigir o que parecia problemático no Express na época — callbacks aninhados, tratamento de erros improvisado e um núcleo que havia acumulado mais funcionalidades do que seus autores desejavam. O Koa 1 utilizava generator functions. O Koa 2 surgiu após o Node 7.6 trazer async/await nativos, e essa é a versão que você usará hoje.

Se o Express te ensina a cadeia de middleware, o Koa te ensina o que acontece quando essa cadeia pode envolver (wrap) uma requisição, em vez de apenas passar por ela.

O objeto de contexto

O Koa agrupa a requisição e a resposta em um único objeto chamado contexto, convencionalmente nomeado como ctx. Em vez de ler de req e escrever em res, você lê e escreve propriedades em um único objeto.

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

A requisição e a resposta ainda estão disponíveis integralmente quando você precisar delas:

  • ctx.request — a mensagem de entrada encapsulada (ctx.request.body, ctx.request.header).
  • ctx.response — a mensagem de saída encapsulada (ctx.response.status).
  • ctx.params, ctx.query e ctx.request.body — roteamento e entrada processada.
  • ctx.state — um objeto simples para passar valores entre middleware, como o usuário autenticado.
  • ctx.throw(status, message) — lança um erro HTTP que o middleware de erro pode capturar.

ctx.state é o lugar idiomático para anexar dados compartilhados. O middleware de autenticação define ctx.state.user, e middlewares posteriores ou a rota o leem, sem poluir o próprio objeto de requisição.

A cebola: middleware que envolve

O middleware do Koa é uma única função async com a assinatura (ctx, next). Chamar await next() passa o controle para a camada interna; qualquer coisa que você escrever após essa linha será executada assim que as camadas internas forem concluídas.

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

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

Este é o modelo de cebola (onion model), e é a ideia central que torna o Koa distinto. Uma cadeia linear só consegue executar código antes da resposta; a cebola consegue executar código em ambos os lados.

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

  await next(); // everything downstream runs here

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

Aquele único middleware cronometra a requisição inteira, incluindo cada rota e cada outro middleware registrado abaixo dele. O mesmo padrão lida com logging, transações de banco de dados e limpeza.

Um middleware também pode interromper a cadeia (short-circuit): se ele definir ctx.body e nunca chamar next(), a requisição termina ali. É assim que a autenticação rejeita uma requisição antes que ela chegue a uma rota.

Roteamento com @koa/router

O Koa não possui um roteador em seu núcleo, portanto, o roteamento é fornecido pelo @koa/router, o pacote mantido pela comunidade.

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());

O router.routes() monta os handlers correspondentes, e o router.allowedMethods() responde com o 405 correto quando o caminho existe, mas o método não. Os roteadores podem ser aninhados e prefixados, o que mantém uma API grande modular da mesma forma que o express.Router() faz.

Tratamento de erros e app.on(“error”)

Como os middleware do Koa são funções async comuns, os erros são exceções comuns. Capture-os em um único lugar envolvendo a chamada downstream.

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);
  }
});

Dentro de um handler, ctx.throw(404, "not found") cria um erro com um status, uma mensagem e uma flag expose que o marca como seguro para ser exibido aos clientes. Erros sem um status tornam-se 500, e sua mensagem é ocultada da resposta.

Nem todo erro pode ser transformado em uma resposta. Se ocorrer uma falha após o envio dos headers, o Koa emitirá um evento error no app:

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

Inscreva-se nesse evento independentemente de qualquer coisa, para que falhas inesperadas sejam sempre registradas.

Por que o Koa não possui roteador ou body parser integrados

O núcleo do Koa é intencionalmente quase vazio. Ele fornece o contexto, o pipeline de middleware e a infraestrutura HTTP; roteamento, body parsing, cookies, sessões, arquivos estáticos e headers de segurança ficam todos em pacotes separados.

Essa é uma escolha deliberada. Um núcleo reduzido é fácil de auditar, possui poucas dependências e evolui lentamente, tornando difícil que o próprio Koa quebre sua aplicação. O custo disso é que você é responsável pela composição: você decide qual body parser adicionar, como processá-lo e onde registrá-lo.

import bodyParser from "koa-bodyparser";

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

A ordem aqui é fundamental, assim como no Express. Um parser registrado após o roteador não estará disponível para as rotas.

Koa ou Express?

Ambos os frameworks compartilham as mesmas ideias de request/response, então a escolha é majoritariamente sobre filosofia.

Escolha o Express quando a familiaridade for a prioridade: ele é o framework Node mais utilizado, já vem com roteamento e possui a maior coleção de middleware. É a escolha padrão mais segura para equipes que precisam de agilidade.

Escolha o Koa quando você preferir um núcleo menor e o modelo de cebola (onion model). O tratamento de erros assíncronos é mais limpo, o objeto de contexto elimina grande parte da manipulação de req/res, e você paga apenas pelo middleware que adicionar. A contrapartida é um ecossistema menor e a necessidade de montar mais coisas por conta própria.

Testando um app Koa

Como um app Koa é um pipeline de middleware, é simples testá-lo com supertest. Exporte o app e passe app.callback() para o helper de requisição para que nenhuma porta seja aberta.

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);
});

Assim como no Express, mantenha a definição do app separada de app.listen() para que os testes possam importá-lo sem iniciar um servidor.

Melhores práticas

  • Mantenha o middleware de tratamento de erros primeiro, para que ele envolva todas as outras camadas.
  • Use ctx.state para valores com escopo de requisição, como o usuário autenticado.
  • Defina ctx.body e ctx.status em vez de manipular a resposta bruta.
  • Registre bodyParser antes do roteador para que o ctx.request.body seja preenchido.
  • Use ctx.throw para erros HTTP esperados e um único handler para os demais.
  • Sempre se inscreva no app.on("error") para registrar falhas que ocorrem após o envio dos headers.
  • Exporte o app separadamente do servidor para que os testes permaneçam rápidos.

Erros comuns

  • Chamar next() duas vezes em um único middleware e executar o código downstream novamente.
  • Esquecer o await antes do next(), o que pula a metade de “retorno” da cebola (onion model).
  • Registrar o router antes do body parser e descobrir que o ctx.request.body está vazio.
  • Assumir que o Koa possui um router ou body parser integrado e importar o pacote errado.
  • Silenciar erros sem emiti-los, não deixando rastros nos logs.
  • Mutar o ctx.res diretamente e ignorar o tratamento de status e headers do Koa.

Próximos passos

O Koa é a demonstração mais limpa de middleware assíncrono no Node.js, e seu modelo de cebola (onion model) está presente em frameworks de diversas linguagens. Se você busca um conjunto de funcionalidades nativas mais amplo e maior performance, leia o guia do Fastify. Se prefere o framework do qual o Koa derivou, revisite o Express. Para entender a camada HTTP por trás de ctx, comece pelo guia de HTTP e mantenha os fundamentos de Node.js por perto.

Na pratica

A cebola em código

Alterne entre as abas para ver como middleware, roteamento e erros se encaixam.

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`);
}

A ordem da cebola

Os middlewares rodam de cima para baixo na entrada e de baixo para cima na saída. Registrar o logger por último significa que ele envolverá apenas o que vier depois dele.

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

Respondendo a uma requisição

Defina ctx.body e deixe o Koa escrever a resposta. Tentar usar a resposta bruta do Node ignora o tratamento de status, headers e erros do Koa.

Preferir
ctx.status = 201;
ctx.body = post;
Evitar
ctx.res.statusCode = 201;
ctx.res.end(JSON.stringify(post));

Trade-offs

O Koa é a base certa para sua API?

O Koa oferece um núcleo pequeno e elegante e deixa o resto para você. Isso é tanto uma força quanto um custo.

Strengths

  • Um núcleo que cabe na cabeça

    O framework tem algumas centenas de linhas. Você pode ler o código-fonte e saber exatamente como uma requisição flui do socket para a resposta.

  • A cebola é genuinamente útil

    Envolver cada requisição com temporização, logs ou uma transação de banco de dados torna-se trivial porque o código após await next() executa na saída.

  • Tratamento de erros async limpo

    Como os middlewares são funções assíncronas, um erro lançado é capturado pelo try/catch mais próximo em vez de desaparecer em uma rejeição não tratada.

Trade-offs

  • Você monta a stack

    Roteamento, parsing de corpo, cookies e arquivos estáticos são todos pacotes separados. Reserve tempo para escolhê-los, configurá-los e mantê-los compatíveis.

  • Ecossistema menor

    Existem menos middlewares específicos para Koa do que para Express, embora a maioria dos pacotes Express tenha um wrapper fino para Koa ou um equivalente direto.

  • Menos orientação para apps grandes

    O Koa não tem opiniões sobre estrutura. As equipes devem concordar com convenções cedo, ou projetos grandes podem derivar para manipuladores inconsistentes.

Perguntas frequentes

Perguntas frequentes

Keep learning

Related topics from the roadmap.

$ comecar a aprender

Pronto para aprender Koa?

Nosso tutorial interativo te guia por Koa passo a passo — com quizzes e codigo real que voce pode executar no navegador.