jlnstack

Schema Validation

Validate and transform route params with Standard Schema

Schema Validation

Routes supports Standard Schema for runtime validation and transformation of route parameters.

Basic Usage

Pass a config with params to createRoutes to enable validation:

import { createRoutes } from "@jlnstack/routes"
import { z } from "zod"

type AppRoutes = "/users/[id]"

const routes = createRoutes<AppRoutes>()({
  "/users/[id]": {
    params: { id: z.coerce.number() },
  },
})

routes.users.id.getRoute({ id: 42 })
// => "/users/42"

The schema's output type is automatically inferred — id expects a number, not a string.

Supported Libraries

Any library implementing the Standard Schema spec works:

Multiple Params

Define schemas for multiple parameters:

type AppRoutes = "/users/[id]/posts/[postId]"

const routes = createRoutes<AppRoutes>()({
  "/users/[id]/posts/[postId]": {
    params: {
      id: z.coerce.number(),
      postId: z.string().uuid(),
    },
  },
})

routes.users.id.posts.postId.getRoute({
  id: 123,
  postId: "550e8400-e29b-41d4-a716-446655440000",
})
// => "/users/123/posts/550e8400-e29b-41d4-a716-446655440000"

Partial Schema Coverage

You don't need schemas for every param. Unspecified params default to string:

type AppRoutes = "/users/[id]" | "/blog/[slug]"

const routes = createRoutes<AppRoutes>()({
  "/users/[id]": { params: { id: z.coerce.number() } },
})

routes.users.id.getRoute({ id: 42 })        // id: number
routes.blog.slug.getRoute({ slug: "post" }) // slug: string

Validation Errors

When validation fails, an error is thrown with details:

type AppRoutes = "/users/[id]"

const routes = createRoutes<AppRoutes>()({
  "/users/[id]": {
    params: { id: z.number().positive() },
  },
})

routes.users.id.getRoute({ id: -1 })
// throws: Validation failed for param "id": Number must be greater than 0

Limitations

  • Async validation is not supported — schemas must validate synchronously
  • Route patterns are inferred from schema map keys when no explicit generic is provided

On this page