# Route Client SDK

> How to use the Uniform Route Client to resolve URL paths into compositions, redirects, or not-found results.

Source: https://docs.uniform.app/docs/sdk/route-client

The Route Client (`RouteClient` from `@uniformdev/canvas`) resolves a URL path using the Uniform project map and redirects in a single call. Given a path, it returns one of three results: the composition attached to the matching project map node, a redirect definition, or a not-found result. Use it to implement routing in custom front ends, middleware, or anywhere you need to answer the question "what should this URL render?"

The client wraps the route endpoint of the Uniform Edge API; see the _For the US region:_

_For the EU region:_

 for the underlying API reference.

---

## Installation

The Route Client is part of the `@uniformdev/canvas` package:

```bash
npm install @uniformdev/canvas
```

---

## Initializing the client

### Standalone usage

```ts
import { RouteClient } from "@uniformdev/canvas";

const routeClient = new RouteClient({
  apiKey: process.env.UNIFORM_API_KEY,
  projectId: process.env.UNIFORM_PROJECT_ID,
  edgeApiHost: "https://uniform.global",
});
```

The Route Client talks to the Uniform edge API. Unlike other clients, it accepts an `edgeApiHost` option (default: `https://uniform.global`) instead of `apiHost`.

| Option | Type | Description |
| --- | --- | --- |
| `apiKey` | `string` | API key with read access to the project |
| `projectId` | `string` | The Uniform project ID |
| `edgeApiHost` | `string` | Edge API host. Default: `https://uniform.global` |
| `disableSWR` | `boolean` | When `true`, disables stale-while-revalidate caching on the edge and always returns fresh data |

### Within the App Router SDK

The App Router SDK provides a pre-configured client via `getRouteClient`:

```ts
import { getRouteClient } from "@uniformdev/next-app-router";

const routeClient = getRouteClient({
  cache: { type: "no-cache" },
});
```

---

## Resolving a route

`get` returns a discriminated union. Check the `type` property to determine the result:

```ts
const route = await routeClient.get({
  path: "/company/about-us",
});

switch (route.type) {
  case "composition": {
    // The path matched a project map node with a composition
    const { composition } = route.compositionApiResponse;
    console.log("Render composition:", composition._name);
    break;
  }

  case "redirect": {
    // The path matched a redirect definition
    console.log(
      "Redirect to:",
      route.redirect.targetUrl,
      "with status",
      route.redirect.targetStatusCode
    );
    break;
  }

  case "notFound": {
    console.log("No matching route");
    break;
  }
}
```

### Query options

`get` accepts these parameters:

| Option | Type | Description |
| --- | --- | --- |
| `path` | `string` | Required. The path to resolve. May include query string parameters (ignored for matching). |
| `projectMapId` | `string` | Project map to resolve against. When omitted, the project's default project map is used. |
| `state` | `number` | Publishing state to fetch: `0` for draft, `64` for published (default). |
| `locale` | `string` | Locale(s) to localize the response to. Accepts a single value (`en-US`), a comma-delimited list (`fr-CA,fr`), or `Accept-Language` header syntax. When omitted, a dynamic `:locale` path segment is used if present; otherwise all locales' data is returned. |
| `releaseId` | `string` | Fetch content as it would appear in the given release. |
| `dataSourceVariant` | `"unpublished"` | When set, data resources that support it fetch unpublished data from their data source. |
| `ignoreRedirects` | `boolean` | When `true`, redirects are not evaluated; the result is either a composition or not found. |
| `withComponentIDs` | `boolean` | When `true`, includes the `_id` of each non-root component in the response. |
| `select` | `ProjectionSpec` | Optional [data projection](#fetching-a-subset-with-select) applied to the resolved composition. |

> **Use an AI coding assistant:**
>
> The [`uniform-sdk`](https://github.com/uniformdev/agent-skills/tree/main/skills/uniform-sdk) agent skill teaches coding assistants such as Claude Code, Codex, Cursor, and Copilot how to authenticate with Uniform, configure the CLI, resolve routes, and read and write compositions, entries, and content types with the content API clients. To install it, see [Uniform agent skills](https://docs.uniform.app/docs/guides/ai/agent-skills).

---

## Route matching

Dynamic project map nodes and redirect wildcards are resolved automatically:

| Requested path | Matches |
| --- | --- |
| `/company/about-us` | `/company/about-us` in the project map or redirects |
| `/products/123` | `/products/:productId` in the project map or `/products/*` in redirects |
| `/products/123?color=red` | `/products/:productId`, with `productId=123` and `color=red` returned as dynamic inputs |

### Conflict resolution

When a path matches multiple project map nodes or redirects:

- Among multiple matches, the most specific path wins: the route with the most path segments first, then the one with the fewest dynamic segments.
- Redirects are evaluated before project map nodes, so when a redirect and a node are equally specific, the redirect wins.

### Dynamic inputs

When the matched route contains dynamic segments (`:productId`), or the project map node allows passed query string parameters, the values are returned in `dynamicInputs`:

```ts
const route = await routeClient.get({
  path: "/products/123?color=red",
});

if (route.type === "composition") {
  console.log(route.matchedRoute); // "/products/:productId"
  console.log(route.dynamicInputs); // { productId: "123", color: "red" }
}
```

---

## Fetching a subset with select

The `select` option applies a data projection to the resolved composition, pruning fields, field types, and slots before values are resolved. This reduces payload size and skips server-side work (asset resolution, rich text expansion, data resource fetches) for content you don't need.

For example, a breadcrumb renderer only needs the title and slug of each page -- not the full component tree:

```ts
const route = await routeClient.get({
  path: "/products/widgets",
  select: {
    fields: { only: ["title", "slug"] },
    slots: { only: [] }, // strip all slots
  },
});
```

Projection only applies when the route resolves to a composition; redirect and not-found responses are returned unchanged.

See [data projection](https://docs.uniform.app/docs/sdk/content-client#selecting-fields-with-data-projection) on the Content Client page for the full projection reference, or the _For the US region:_

_For the EU region:_

 for the wire-level `select.*` syntax.

---

## Import reference

| Export | Package | Description |
| --- | --- | --- |
| `RouteClient` | `@uniformdev/canvas` | Main route resolution client |
| `ResolvedRouteGetResponse` | `@uniformdev/canvas` | Union type of the three route results |
| `ProjectionSpec` | `@uniformdev/canvas` | Type for the `select` data projection option |
| `getRouteClient` | `@uniformdev/next-app-router` | Pre-configured route client (server-only, App Router) |
