lee-AYZ — to act as the link between two parties.
Your API calls, minus the surprises.
Type-safe REST and GraphQL on plain fetch. Never throws. Zero dependencies. Works with any framework.
npm install liaiseDocumentation · Playground · How it compares
Formerly published as @iremlopsum/apify; switching takes two steps, see MIGRATION.md.
fetch is a good building block. Every project still ends up writing the same few things around it, and they are easy to get subtly wrong.
| With plain fetch | liaise | See |
|---|---|---|
| Typing fast in a search box shows old results. A slow early search lands last. | dedupe cancels the older call. |
Stale requests |
| Five components load the same data, or five 401s each refresh the token. That's five identical requests. | share sends one and hands everyone the answer. |
Sharing identical requests |
| A 500 counts as success, offline throws, a hung server waits forever. | Every call returns { data, error }, and error.kind names the failure. With timeout set, a hung server becomes an error too. |
Handling errors |
| Retries run straight past your timeout. | timeout covers the whole operation, retries included. |
Deadlines |
| The backend changes a field and the page crashes three components later. | A schema checks the response. A bad shape is an error you handle. | Validating responses |
The problem it solves shows one form submit written both ways.
Define two endpoints, create a client, and make a call.
import { createApi, defineRequest } from 'liaise'
type User = { id: string; name: string; email: string }
// 1. Describe your endpoints. The path decides which params are required.
const getUser = defineRequest<User>()({ method: 'GET', path: '/users/:id' })
const createUser = defineRequest<User, { name: string; email: string }>()({
method: 'POST',
path: '/users',
})
// 2. Create the client. There is no default timeout: without one, a server
// that never answers keeps the call waiting.
const api = createApi({
baseUrl: 'https://api.example.com',
requests: { getUser, createUser },
timeout: 10_000,
})
// 3. Call it. This never throws: you always get { data, error }.
const { data, error } = await api.getUser({ id: '42' })
if (error) {
// error.kind says what went wrong: 'http', 'network', 'timeout', ...
console.error(error.kind, error.status)
} else {
console.log(data.name) // data is a User here
}
// A POST sends its params as a JSON body.
const created = await api.createUser({ name: 'Grace', email: 'grace@example.com' })
if (!created.error) console.log(created.data.id)- The params are checked against the path, so
getUser({ userId: '42' })is a compile error. datais typed fromdefineRequest<User>.- Nothing throws, not even when you're offline.
- Checking
errorfirst narrowsdatatoUser, so you never writedata!.
Try it without installing anything in the playground.
Everything else lives on the docs site, iremlopsum.github.io/liaise, with search.
- Getting started: the problem it solves, the quick start, and how the pieces fit together.
- Guide: defining endpoints, handling errors, sending data, reading and validating responses, deadlines and stale requests, sharing identical requests, retries, caching and logging, middleware, pagination, polling, GraphQL, and testing your code.
- Recipes: a token refresh on 401, search as you type, TanStack Query, React, a store, server loaders, a flaky backend, per-attempt timeouts, Sentry, file uploads and downloads, and a query param with a JSON body.
- Choosing liaise: when it fits and when it doesn't, how it compares with axios, ky and ofetch, and where it runs.
- Reference: every option,
ResultandApiError, middleware context,liaise/testing, the exports, and behaviour in detail.
- Upgrading. MIGRATION.md says what to change when an upgrade needs it. CHANGELOG.md lists every release. The docs site covers the latest version; for 5.1.1 and earlier, read the README at that version's tag, such as 5.1.1's.
- Contributing. Bug reports, fixes and ideas are welcome. CONTRIBUTING.md explains how to report a bug, run the tests, and the few rules a pull request is checked against.
- Licence. MIT, in LICENSE.