Skip to content

Handler

Each route handler receives a Context object (usually called c) that gives you access to request data and response helpers.

The Context Object

typescript
app.get("/example", async (c) => {
  // c is your Context — it has everything you need
});

Request Properties

Access basic request info directly:

typescript
app.get("/info", (c) => {
  return {
    method: c.method, // "GET", "POST", etc.
    path: c.path, // "/info"
    headers: c.headers, // Headers object
  };
});

Path Parameters

Get dynamic path segments with c.param():

typescript
// GET /users/42
app.get("/users/:id", (c) => {
  const id = c.param("id"); // "42"
  return { id };
});

// GET /posts/123/comments/456
app.get("/posts/:postId/comments/:commentId", (c) => {
  const postId = c.param("postId"); // "123"
  const commentId = c.param("commentId"); // "456"
  return { postId, commentId };
});

Returns null if the parameter doesn't exist.

Query Parameters

Get URL query string values with c.query():

typescript
// GET /search?q=kyrin&limit=10
app.get("/search", (c) => {
  const q = c.query("q"); // "kyrin"
  const limit = c.query("limit"); // "10"
  const page = c.query("page"); // null (not present)
  return { q, limit };
});

Headers

Read request headers:

typescript
app.get("/auth", (c) => {
  const auth = c.header("Authorization");
  const contentType = c.header("Content-Type");

  if (!auth) {
    return c.json({ error: "No auth header" }, 401);
  }

  return { auth };
});

The c.headers property gives you the full Headers object if you need it.

Body Parsing

JSON Body

Parse JSON request body with type safety:

typescript
app.post("/users", async (c) => {
  // With type parameter
  const body = await c.body<{ name: string; email: string }>();
  return { received: body };
});

// Or destructure directly
app.post("/users", async (c) => {
  const { name, email } = await c.body<{ name: string; email: string }>();
  return { name, email };
});

Constraints

Use c.body() and define (c: any) to parse JSON request body with flexible type:

typescript
app.post("/users", async (c:any) => {
  const body = await c.body();
  return { received: body };
});

app.post("/users", async (c: any) => {
  const body = await c.body();
  const response = `Hello ${body.name}!`;
  return { message: response };
});

// minimal version
app.post("/users", async (c: any) => ({
  message: `Hello ${(await c.body()).name}!`,
}));

Force await and async function to do this!!

Plain Text

Get raw text body:

typescript
app.post("/webhook", async (c) => {
  const text = await c.text();
  return { length: text.length };
});

Form Data

Handle form submissions:

typescript
app.post("/upload", async (c) => {
  const form = await c.formData();
  const name = form.get("name");
  const file = form.get("file") as File;

  return {
    name,
    filename: file?.name,
    size: file?.size,
  };
});

Response Helpers

JSON Response

Return JSON with optional status code:

typescript
app.get("/data", (c) => {
  return c.json({ message: "Hello" });
});

app.get("/error", (c) => {
  return c.json({ error: "Something went wrong" }, 500);
});

Plain Text

Send plain text:

typescript
app.get("/text", (c) => {
  return c.send("Hello, plain text!");
});

app.get("/error", (c) => {
  return c.send("Error occurred", 500);
});

HTML

Return HTML content:

typescript
app.get("/page", (c) => {
  return c.html("<h1>Welcome</h1><p>This is HTML</p>");
});

app.get("/error", (c) => {
  return c.html("<h1>404</h1><p>Not found</p>", 404);
});

Redirect

Redirect to another URL:

typescript
app.get("/old-page", (c) => {
  return c.redirect("/new-page"); // 302 by default
});

app.get("/permanent", (c) => {
  return c.redirect("/new-location", 301); // Permanent redirect
});

Not Found

Return a 404 response:

typescript
app.get("/users/:id", (c) => {
  const user = findUser(c.param("id"));
  if (!user) {
    return c.notFound();
  }
  return user;
});

Setting Response Headers

Use c.set.headers to add headers to the response:

typescript
app.get("/data", (c) => {
  c.set.headers["X-Request-Id"] = "abc123";
  c.set.headers["Cache-Control"] = "max-age=3600";

  return { data: "with custom headers" };
});

Setting Status Code

Set the status code before returning:

typescript
app.post("/users", async (c) => {
  const body = await c.body();
  // ... create user

  c.set.status = 201; // Created
  return { id: 1, ...body };
});

Or pass it directly to response helpers:

typescript
app.post("/users", async (c) => {
  const body = await c.body();
  return c.json({ id: 1, ...body }, 201);
});

The Store

The store is a simple object for passing data between middleware and handlers:

typescript
// Middleware sets data
app.use(async (c, next) => {
  c.store.requestId = crypto.randomUUID();
  c.store.startTime = Date.now();
  await next();
});

// Handler reads it
app.get("/data", (c) => {
  return {
    requestId: c.store.requestId,
    data: "something",
  };
});

TypeScript users: store is typed as Record<string, unknown>, so you may need to cast values.

Raw Request

Access the underlying Request object when you need it:

typescript
app.post("/raw", async (c) => {
  const contentLength = c.req.headers.get("Content-Length");
  const arrayBuffer = await c.req.arrayBuffer();

  return { size: arrayBuffer.byteLength };
});

Complete Example

typescript
import { Kyrin } from "kyrin";

const app = new Kyrin();

// Simple GET
app.get("/", () => ({ message: "Hello!" }));

// Path params
app.get("/users/:id", (c) => {
  const id = c.param("id");
  return { userId: id };
});

// Query params
app.get("/search", (c) => {
  const q = c.query("q") || "";
  const page = parseInt(c.query("page") || "1", 10);
  return { query: q, page };
});

// JSON body
app.post("/users", async (c) => {
  const { name, email } = await c.body<{ name: string; email: string }>();
  c.set.status = 201;
  return { id: 1, name, email };
});

// Custom headers
app.get("/download", (c) => {
  c.set.headers["Content-Disposition"] = "attachment; filename=data.json";
  return { data: "example" };
});

// Redirect
app.get("/github", (c) => {
  return c.redirect("https://github.com");
});

// 404
app.get("/maybe/:id", (c) => {
  const id = c.param("id");
  if (id === "nothing") {
    return c.notFound();
  }
  return { found: id };
});

app.listen(3000);

Released under the MIT License.