npm package: @routepact/core
Description: Type-safe route spec definitions shared between server and client
Latest version: 0.1.13

# @routepact/core

Shared types and utilities for defining type-safe route pacts. This package is the contract between your server and client - import it in shared code that both sides depend on.

## Installation

```bash
npm install @routepact/core
```

You also need a schema library that implements the [Standard Schema](https://standardschema.dev/) interface (e.g. Zod, Valibot, ArkType). Examples below use Zod but any Standard Schema-compatible library works.

```bash
npm install zod    # or valibot, arktype, etc.
```

## Defining a pact

A pact describes a single endpoint: its path, HTTP method, and optional validation schemas for the request body, response body, query parameters, path parameters, and response metadata. Schemas must implement the [Standard Schema](https://standardschema.dev/) interface.

Use `definePact` to get full type inference while keeping literal types (exact path strings, method literals):

```ts
import { z } from "zod";
import { definePact } from "@routepact/core";

export const PostPacts = {
  list: definePact({
    method: "get",
    path: "/posts",
    response: z.object({
      items: z.object({ id: z.string(), title: z.string() }).array(),
      total: z.number(),
      page: z.number(),
    }),
    query: z.object({
      page: z.string().optional(),
      limit: z.string().optional(),
    }),
  }),
  getById: definePact({
    method: "get",
    path: "/posts/:id",
    response: z.object({
      id: z.string(),
      title: z.string(),
      body: z.string(),
    }),
  }),
  create: definePact({
    method: "post",
    path: "/posts",
    request: z.object({ title: z.string(), body: z.string() }),
    response: z.object({
      id: z.string(),
      title: z.string(),
      body: z.string(),
    }),
  }),
  update: definePact({
    method: "patch",
    path: "/posts/:id",
    request: z.object({
      title: z.string().optional(),
      body: z.string().optional(),
    }),
    response: z.object({
      id: z.string(),
      title: z.string(),
      body: z.string(),
    }),
  }),
  delete: definePact({
    method: "delete",
    path: "/posts/:id",
  }),
};
```

## Validation schemas

| Field      | Required for                    | Description                                                       |
| ---------- | ------------------------------- | ----------------------------------------------------------------- |
| `request`  | Only for `post`, `patch`, `put` | Object schema for `req.body` on the server                        |
| `response` | Optional                        | Object schema for the full response body sent via `res.json(...)` |
| `query`    | Optional                        | Object schema for query parameters - typed on both server and client |

All validation schemas must describe object types (e.g. `z.object(...)` in Zod) - routepact is opinionated that request bodies, responses, and query parameters are always objects. Primitive or array schemas are not supported at the pact level.

## Path parameters

Parameters in the path (`:param`) are extracted as a type-safe object. If a path contains `:id`, the `params` option is required and typed as `{ id: string }`.

```ts
// TypeScript requires params when the path has parameters
await request(PostPacts.getById, { params: { id: "abc" } });

// TypeScript forbids params when there are none
await request(PostPacts.list);
```

On the server, `ctx.params` is automatically populated from the URL and typed as `{ [key: string]: string }` based on the path string - no Zod schema needed.

## Query parameters

Add a `query` schema to make query parameters type-safe on both server and client. If any field in the schema is required, TypeScript will require the `query` option at the call site:

```ts
const list = definePact({
  method: "get",
  path: "/posts",
  query: z.object({ page: z.string().optional(), sort: z.string() }), // sort is required
});

// TypeScript requires query.sort
await request(list, { query: { sort: "createdAt" } });
```

## Type reference

| Type                          | Description                                                                                       |
| ----------------------------- | ------------------------------------------------------------------------------------------------- |
| `RoutePact`                   | Generic type for a single pact definition                                                         |
| `AnyRoutePact`                | Widened pact type - used internally and in package integrations                                   |
| `AnyRouteValidation`          | The set of optional Standard Schema-compatible schemas that can be attached to a pact             |
| `HttpMethod`                  | `"get" \| "post" \| "patch" \| "put" \| "delete"`                                                 |
| `RouteOptions<TPact>`         | Inferred call-site options (params, payload, query) for a pact                                    |
| `RouteOptionsRequired<TPact>` | `true` if options are required (pact has params or a request schema)                              |
| `QueryOption<TPact>`          | Inferred `query` option - required if the query schema has required fields                        |
| `PactRequest<TPact>`          | Inferred output type of the request body - `never` if the pact has no `request` schema            |
| `PactInputRequest<TPact>`     | Inferred input type of the request body (before transforms) - `never` if no `request` schema      |
| `PactParams<TPact>`           | Inferred path parameter record - `never` if the path has no `:param` segments                     |
| `PactQuery<TPact>`            | Inferred output type of query parameters - `never` if the pact has no `query` schema              |
| `PactInputQuery<TPact>`       | Inferred input type of query parameters (before transforms) - `never` if no `query` schema        |
| `PactResponse<TPact>`         | Inferred response type - the schema's output type when a response schema is defined, `never` otherwise |
| `ExtractParams<TPath>`        | Extracts parameter names from a path string                                                       |
| `ExpectedParams<TPath>`       | Maps extracted param names to `string` values                                                     |