Getting Started

This guide walks you through building your first Mage application. We'll create a simple task API to demonstrate core concepts like routing, middleware, and request handling.

Prerequisites

Make sure you've installed Mage first.

Create Your Application

Create a new file main.ts:

import { MageApp } from "@mage/app";

const app = new MageApp();

// We'll add routes here

Deno.serve(app.handler);

Adding Routes

Let's start with a simple GET route that returns a list of tasks:

import { MageApp } from "@mage/app";

const app = new MageApp();

const tasks = [
  { id: 1, title: "Learn Mage", completed: false },
  { id: 2, title: "Build an API", completed: false },
];

app.get("/tasks", (c) => {
  return c.json(tasks);
});

Deno.serve(app.handler);

Run it:

deno run --allow-net main.ts

Visit http://localhost:8000/tasks and you'll see your tasks as JSON.

Understanding Context

Every route handler receives a MageContext object (typically named c). This gives you access to:

  • c.req - The request (headers, body, params, etc.)
  • c.res - The response (set headers, status, etc.)
  • Helper methods - c.json(), c.text(), c.html(), c.redirect(), etc.

Route Parameters

Add a route to get a specific task by ID:

app.get("/tasks/:id", (c) => {
  const id = parseInt(c.req.params.id);
  const task = tasks.find((t) => t.id === id);

  if (!task) {
    return c.json({ error: "Task not found" }, 404);
  }

  return c.json(task);
});

Try http://localhost:8000/tasks/1 to get the first task.

Handling Request Bodies

Let's add a POST route to create tasks:

app.post("/tasks", async (c) => {
  const body = await c.req.json();

  const newTask = {
    id: tasks.length + 1,
    title: body.title,
    completed: false,
  };

  tasks.push(newTask);

  return c.json(newTask, 201);
});

Test it with curl:

curl -X POST http://localhost:8000/tasks \
  -H "Content-Type: application/json" \
  -d '{"title":"Deploy to production"}'

Using Middleware

Middleware runs before your route handlers. Let's add CORS support and body size limiting:

import { MageApp } from "@mage/app";
import { cors } from "@mage/app/cors";
import { bodySize } from "@mage/app/body-size";

const app = new MageApp();

// Global middleware - runs on every request
app.use(cors({ origins: "*" }));
app.use(bodySize({ maxSize: 1024 * 1024 })); // 1MB limit

// Your routes here...

You can also apply middleware to specific routes:

import { requestId } from "@mage/app/request-id";

app.get("/tasks", requestId(), (c) => {
  console.log(`Request ID: ${c.get("requestId")}`);
  return c.json(tasks);
});

Error Handling

Use MageError to throw HTTP errors:

import { MageApp, MageError } from "@mage/app";

app.delete("/tasks/:id", (c) => {
  const id = parseInt(c.req.params.id);
  const index = tasks.findIndex((t) => t.id === id);

  if (index === -1) {
    throw new MageError("Task not found", 404);
  }

  tasks.splice(index, 1);
  return c.empty(204);
});

For comprehensive error handling patterns including error handler middleware, see Error Handling.

Complete Example

Here's the full application:

import { MageApp, MageError } from "@mage/app";
import { cors } from "@mage/app/cors";
import { bodySize } from "@mage/app/body-size";

const app = new MageApp();

// Middleware
app.use(cors({ origins: "*" }));
app.use(bodySize({ maxSize: 1024 * 1024 }));

// In-memory storage
const tasks = [
  { id: 1, title: "Learn Mage", completed: false },
  { id: 2, title: "Build an API", completed: false },
];

// Routes
app.get("/tasks", (c) => {
  return c.json(tasks);
});

app.get("/tasks/:id", (c) => {
  const id = parseInt(c.req.params.id);
  const task = tasks.find((t) => t.id === id);

  if (!task) {
    throw new MageError("Task not found", 404);
  }

  return c.json(task);
});

app.post("/tasks", async (c) => {
  const body = await c.req.json();

  const newTask = {
    id: tasks.length + 1,
    title: body.title,
    completed: false,
  };

  tasks.push(newTask);
  return c.json(newTask, 201);
});

app.delete("/tasks/:id", (c) => {
  const id = parseInt(c.req.params.id);
  const index = tasks.findIndex((t) => t.id === id);

  if (index === -1) {
    throw new MageError("Task not found", 404);
  }

  tasks.splice(index, 1);
  return c.empty(204);
});

Deno.serve(app.handler);

Next Steps

You've learned the basics! Here's what to explore next: