# Route handlers

Create a `route.ts` file under `app/` to expose an HTTP endpoint. A route handler is a server-side
function that receives a standard [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request)
and returns a standard [Response](https://developer.mozilla.org/en-US/docs/Web/API/Response):

```ts
// app/api/hello/route.ts
export async function GET() {
  return Response.json({ message: "hello" })
}

export async function POST(request: Request) {
  const body = await request.json()
  return Response.json({ received: body }, { status: 201 })
}
```

A segment cannot contain both `page.tsx` and `route.ts`. If both would serve the same URL, the build
fails.

Route handlers can have [dynamic segments](./introduction.md#dynamic-segments).

## Methods and signature

Export one or more named HTTP method functions. Supported methods are `GET`, `POST`, `PUT`, `PATCH`,
`DELETE`, `HEAD`, and `OPTIONS`.

Each method receives the request and a context object containing the matched route parameters. The
handler must return a [Response](https://developer.mozilla.org/en-US/docs/Web/API/Response):

```ts
export async function GET(
  request: Request,
  { params }: { params: { id: string } },
) {
  const url = new URL(request.url)
  return Response.json({ id: params.id, tab: url.searchParams.get("tab") })
}
```

Static routes receive an empty `params` object. When a route has dynamic segments, type `params` with
the names of those segments. Named params are strings. Catch-all params, such as `[...slug]`, are
arrays of strings.

Returning any value other than a `Response` results in a `500` response.

## Cookies in Route Handlers

Use `cookies(request)` to read request cookies and `cookies(response)` to set response cookies:

```ts
import { cookies } from "@rebask/core/cookies"

export async function POST(request: Request) {
  const session = cookies(request).get("session")
  const response = Response.json({ authenticated: Boolean(session) })

  cookies(response).set("visited", "true", {
    httpOnly: true,
    path: "/",
  })

  return response
}
```

See [`cookies`](../server-runtime-apis.md#cookies) in the Server Runtime APIs docs for the available options.

## Automatic methods and errors

- `HEAD` runs an explicit `HEAD` handler when one exists. Otherwise, it runs `GET`, preserves its status and headers, and removes the response body.
- `OPTIONS` runs an explicit `OPTIONS` handler when one exists. Otherwise, it returns `204` with an `Allow` header listing the implemented methods plus automatic `HEAD` and `OPTIONS` where applicable.
- A supported method that is not exported returns `405`.
- An unrecognized method such as `TRACE` does not match a route handler and returns `404`.

## Caching and invalidation

Responses are not cached automatically. The `Cache-Control` header can be used to enable caching.
Use `s-maxage` to define how long shared caches consider it fresh and `stale-while-revalidate` to
allow stale responses while refreshing in the background:

```ts
export async function GET() {
  return Response.json(
    { products: await getProducts() },
    {
      headers: {
        "cache-control": "public, s-maxage=300, stale-while-revalidate=3600",
      },
    },
  )
}
```

Route handlers can also trigger cache invalidation. For example, a protected endpoint can call
[`revalidateTag()`](../caching/cache-lifetimes.md#revalidatetag) to invalidate tagged server-side
data:

```ts
import { revalidateTag } from "@rebask/core/cache"

export async function POST(request: Request) {
  const { tag } = await request.json()
  await revalidateTag(tag)
  return Response.json({ revalidated: tag })
}
```
