Skip to content

TypeScript client

A TypeScript client, @tourpingo/client, is built (issue #486) and generated from the published contract, so its types are the API’s. It is a small fetch client with no runtime dependencies:

  • createClient({ baseUrl, publishableKey }) makes a client; a server can pass a secret key instead.
  • call(operationId, input) takes an operation’s id from the spec and its path, query and header parameters and body, all typed, with the API’s snake_case field names.
  • It never throws for an answer: you get ok: true with status and typed data, or ok: false with status and the problem. A network failure is a problem with status 0 and the code NetworkError.

It isn’t published to npm yet; that is still to be decided. When it is, this page will say how to install it.

Every example in these guides has a TypeScript tab that uses fetch, which Node, Bun, Deno and Cloudflare Workers all have. To type the answers, you can generate types from the spec with any OpenAPI 3.1 tool:

Wrap fetch once, so every call sends the key and every error becomes one type you can switch on by code:

type Problem = { status: number; code: string; detail: string; [field: string]: unknown }
export class TourpingoError extends Error {
constructor(readonly problem: Problem) {
super(`${problem.code}: ${problem.detail}`)
}
}
export const tourpingo = async <T>(path: string, init: RequestInit = {}): Promise<T> => {
const res = await fetch(new URL(path, process.env.TP_API_URL), {
...init,
headers: { "Tp-Publishable-Key": process.env.TP_KEY ?? "", "Content-Type": "application/json", ...init.headers },
})
const body = res.status === 204 ? null : await res.json()
if (!res.ok) throw new TourpingoError(body as Problem)
return body as T
}