Aura Router
Concepts

Endpoint Definitions

Create endpoint definitions in Aura Router

An endpoint definition is the source of truth for a request's TypeScript types. It's a declarative object that describes the request and response shape, along with any parameters, search params, headers, and body that may be required.

An endpoint defines the action or resource a request targets. It must define the HTTP method, the URL path, and the handler function that runs when the request is matched. The handler is responsible for processing the request and returning a response.

import { createEndpoint } from "@aura-stack/router"

const getSession = createEndpoint("GET", "/auth/session", async (ctx) => {
  return ctx.json({
    session: {
      user: {
        userId: "uuid-123",
        username: "john_doe",
      },
    },
  })
})

Use ctx.json instead of Response.json when possible. It preserves the typed response payload, automatically sets the Content-Type header, and gives you full inference on the client via createClient. See RequestContext for details.

Request Context

The handler function takes a RequestContext object as its argument. It contains information about the request body, headers, params, search params and plus helpers for building the response.

import { createEndpoint } from "@aura-stack/router"

const getSession = createEndpoint("GET", "/auth/session", async (ctx) => {
  if (!ctx.headers.has("Authorization")) {
    return ctx.json({ error: "Unauthorized" }, 401)
  }

  return ctx.json({
    session: {
      user: {
        userId: "uuid-123",
        username: "john_doe",
      },
    },
  })
})

A one-off check like this is fine inline, but if the same guard needs to run across several endpoints, define it once as an onRequest hook or a use middleware instead of repeating it in every handler. See Lifecycle - onRequest.

Configuring Endpoints

The endpoint definition can also include an optional configuration object to customize its behavior. The configuration object can include:

  • schemas: Validation schemas for validating and typing params, searchParams, body, and headers. See Schema Validation.
  • use: middlewares that run once, right before the handler, with access to the fully parsed and validated context. Best suited for cross-cutting logic like auth or logging that several endpoints share.
  • hooks: lifecycle hooks tied to a specific phase of the request/response cycle (onRequest, onParams, onBody, onResponse, and more). Best suited for transforms scoped to a single field, right as it's parsed. See Lifecycle.

The configuration can be defined in two ways: passed directly to createEndpoint, or built separately with createEndpointConfig.

import { z } from "zod"
import { createEndpoint } from "@aura-stack/router"
import { hashPassword } from "@/lib/helpers"

const createUser = createEndpoint(
  "POST",
  "/users",
  async (ctx) => {
    const body = ctx.body
    return ctx.json({ user: body })
  },
  {
    schemas: {
      body: z.object({
        username: z.string().min(1),
        email: z.string().email(),
        password: z.string().min(8),
      }),
    },
    hooks: {
      onBody: async ({ body }) => {
        body.password = await hashPassword(body.password)
      },
    },
  }
)
import { z } from "zod"
import { createEndpoint, createEndpointConfig } from "@aura-stack/router"

const config = createEndpointConfig({
  schemas: {
    body: z.object({
      username: z.string().min(1),
      email: z.string().email(),
      password: z.string().min(8),
    }),
  },
  hooks: {
    onBody: async ({ body }) => {
      body.password = await hashPassword(body.password)
    },
  },
})

const createUser = createEndpoint(
  "POST",
  "/users",
  async (ctx) => {
    const body = ctx.body
    return ctx.json({ user: body })
  },
  config
)

Lifecycle Execution Order: The onBody hook receives the parsed but unvalidated payload and executes before schema validation. This means your schema will validate the mutated (hashed) password. If you need to validate the original plaintext password (e.g., enforcing a minimum length of 8 characters), move your hashing logic to the onHandler hook or directly inside the endpoint handler, which executes after schema validation succeeds.

On this page