Route Client SDK

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 OpenAPI specification for the underlying API reference.


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

npm install @uniformdev/canvas

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.

OptionTypeDescription
apiKeystringAPI key with read access to the project
projectIdstringThe Uniform project ID
edgeApiHoststringEdge API host. Default: https://uniform.global
disableSWRbooleanWhen true, disables stale-while-revalidate caching on the edge and always returns fresh data

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

import { getRouteClient } from "@uniformdev/next-app-router"; const routeClient = getRouteClient({ cache: { type: "no-cache" }, });

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

const route = await routeClient.getRoute({ 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; } }

getRoute accepts these parameters:

OptionTypeDescription
pathstringRequired. The path to resolve. May include query string parameters (ignored for matching).
projectMapIdstringProject map to resolve against. When omitted, the project's default project map is used.
statenumberPublishing state to fetch: 0 for draft, 64 for published (default).
localestringLocale(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.
releaseIdstringFetch 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.
ignoreRedirectsbooleanWhen true, redirects are not evaluated; the result is either a composition or not found.
withComponentIDsbooleanWhen true, includes the _id of each non-root component in the response.
selectProjectionSpecOptional data projection applied to the resolved composition.

Dynamic project map nodes and redirect wildcards are resolved automatically:

Requested pathMatches
/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

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.

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

const route = await routeClient.getRoute({ path: "/products/123?color=red", }); if (route.type === "composition") { console.log(route.matchedRoute); // "/products/:productId" console.log(route.dynamicInputs); // { productId: "123", color: "red" } }

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:

const route = await routeClient.getRoute({ 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 on the Content Client page for the full projection reference, or the route endpoint OpenAPI specification for the wire-level select.* syntax.


ExportPackageDescription
RouteClient@uniformdev/canvasMain route resolution client
ResolvedRouteGetResponse@uniformdev/canvasUnion type of the three route results
ProjectionSpec@uniformdev/canvasType for the select data projection option
getRouteClient@uniformdev/next-app-routerPre-configured route client (server-only, App Router)