The Request Object

ctx.request Exposes Method, URL, Headers, and Content Negotiation

The Request Object

ctx.request wraps Node's IncomingMessage with convenient getters for method, path, query, headers, IP address, and content-negotiation helpers.

3 min read Level 1/5 #koa#request#ctx.request
What you'll learn
  • Access method, url, path, query, and headers from ctx.request
  • Retrieve the client IP address safely with ctx.request.ip
  • Use ctx.request.is() and ctx.request.accepts() for content negotiation

ctx.request is Koa’s enriched wrapper around Node’s raw IncomingMessage. Most of its properties are also available as direct aliases on ctx (e.g., ctx.method === ctx.request.method), but accessing them through ctx.request makes the intent explicit.

Method, URL, and Path

app.use(async (ctx) => {
  console.log(ctx.request.method);      // "GET", "POST", etc.
  console.log(ctx.request.url);         // "/items?page=2"
  console.log(ctx.request.path);        // "/items"
  console.log(ctx.request.querystring); // "page=2"
});

Query String

ctx.request.query parses the query string into a plain object for you. It is always an object, even when there is no query string (returns {}):

// GET /search?q=koa&limit=10
app.use(async (ctx) => {
  const { q, limit } = ctx.request.query;
  ctx.body = `Searching for "${q}", limit ${limit}`;
});

Headers

app.use(async (ctx) => {
  const auth = ctx.request.get("Authorization");
  const ua   = ctx.request.headers["user-agent"];
  ctx.body = { auth, ua };
});

ctx.request.get(field) is a case-insensitive header getter. You can also read ctx.request.headers directly for the full headers object.

Client IP Address

app.use(async (ctx) => {
  ctx.body = `Your IP: ${ctx.request.ip}`;
});

When app.proxy = true, Koa reads X-Forwarded-For to resolve the real client IP. Without it, ctx.request.ip falls back to the socket’s remote address.

Content Negotiation

ctx.request.is() checks the Content-Type of the incoming request:

app.use(async (ctx, next) => {
  if (!ctx.request.is("application/json")) {
    ctx.throw(415, "JSON required");
  }
  await next();
});

ctx.request.accepts() inspects the Accept header to determine what the client can receive:

app.use(async (ctx) => {
  if (ctx.request.accepts("json")) {
    ctx.type = "json";
    ctx.body = { hello: "world" };
  } else {
    ctx.type = "text";
    ctx.body = "hello world";
  }
});

Up Next

Learn how to shape responses with ctx.response.

The Response Object →