npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@routepact/client

v0.1.13

Published

Ky-based type-safe HTTP client for route pacts - validates responses against Zod schemas

Downloads

1,473

Readme

@routepact/client

ky-based HTTP client for @routepact/core pacts. Pass a pact and get back a fully-typed response - params, payload, queries, and return type are all inferred automatically.

Installation

npm install @routepact/client @routepact/core ky

You also need a schema library that implements the Standard Schema interface (e.g. Zod, Valibot, ArkType) for defining your pacts. Examples below use Zod but any Standard Schema-compatible library works.

npm install zod    # or valibot, arktype, etc.

Setup

Create a request function by binding a ky instance and a base URL. You typically do this once and export it for use across your app.

import ky from "ky";
import { createRequest } from "@routepact/client";

const api = ky.create({
  headers: { "Content-Type": "application/json" },
  credentials: "include",
});

export const request = createRequest(api, "https://api.example.com");

Making requests

Pass a pact to request. TypeScript infers everything from the pact - what options are required, what the return type is, and whether params, payload, or query are needed.

import { PostPacts } from "../shared/pacts/post.pact";

// GET /posts - no options needed
const posts = await request(PostPacts.list);
// posts: { id: string; title: string }[]

// GET /posts/:id - params are required
const post = await request(PostPacts.getById, {
  params: { id: "abc" },
});
// post: { id: string; title: string; body: string }

// POST /posts - payload is required, typed from the request schema
const created = await request(PostPacts.create, {
  payload: { title: "Hello", body: "World" },
});
// created: { id: string; title: string; body: string }

// PATCH /posts/:id - both params and payload
const updated = await request(PostPacts.update, {
  params: { id: "abc" },
  payload: { title: "Updated title" },
});

// DELETE /posts/:id - params required, no response body
await request(PostPacts.delete, { params: { id: "abc" } });

Query parameters

Query parameters are typed from the pact's query schema. If the schema has required fields, TypeScript will require query at the call site:

// pact defined with: query: z.object({ page: z.string().optional(), sort: z.string() })
const posts = await request(PostPacts.list, {
  query: { sort: "createdAt", page: "2" },
});
// → GET /posts?sort=createdAt&page=2

If the pact has no query schema, query accepts never and TypeScript will prevent you from passing it.

Customising the ky instance

Since createRequest accepts any KyInstance, you can configure ky however you like before passing it in - hooks, auth headers, retry logic, etc.

import ky from "ky";
import { createRequest } from "@routepact/client";

const api = ky.create({
  prefixUrl: "https://api.example.com",
  retry: { limit: 2 },
  hooks: {
    beforeRequest: [
      (request) => {
        request.headers.set("Authorization", `Bearer ${getToken()}`);
      },
    ],
  },
});

export const request = createRequest(api, "");
// baseUrl is empty because prefixUrl is set on the ky instance

You can also pass per-request ky hooks via the hooks option:

const post = await request(PostPacts.getById, {
  params: { id: "abc" },
  hooks: {
    beforeRequest: [(req) => req.headers.set("X-Trace-Id", traceId)],
  },
});

Response validation

If the pact defines a response schema, the client validates the data before returning it. If validation fails, a ClientValidationError is thrown:

import { ClientValidationError } from "@routepact/client";

try {
  const post = await request(PostPacts.getById, {
    params: { id: "abc" },
  });
} catch (err) {
  if (err instanceof ClientValidationError) {
    console.error(err.cause); // Standard Schema issues array
  }
}

If the pact has no response schema, the return type is never and no validation runs.

Multiple API instances

You can create multiple request functions pointing to different APIs:

export const internalRequest = createRequest(
  internalKy,
  "https://internal.example.com",
);
export const externalRequest = createRequest(
  externalKy,
  "https://api.partner.com",
);

API reference

createRequest(kyInstance, baseUrl)

Returns an async function with the signature:

<TPact extends AnyRoutePact>(
  pact: TPact,
  options?: ClientRequestOptions<TPact>,
) => Promise<PactResponse<TPact>>;
  • pact - a pact created with definePact from @routepact/core
  • options.params - required when the path contains :param segments
  • options.payload - required for post, patch, put when the pact has a request schema
  • options.query - typed from the pact's query schema; required if the schema has required fields
  • options.hooks - optional ky hooks for this specific request
  • Returns PactResponse<TPact> - the schema's output type when a response schema is defined, undefined otherwise

Type reference

| Export | Description | | ----------------------------- | -------------------------------------------------------------------------------- | | createRequest(ky, baseUrl) | Creates a typed request function bound to a ky instance and base URL | | ClientRequestOptions<TPact> | Options accepted by the request function - params, payload, query, hooks | | ClientValidationError | Thrown when response or meta validation fails - has field and cause |