@polygonlabs/bpn-rest-api
v0.3.0
Published
BPN REST API — OpenAPI spec, Zod schemas, and TypeScript client
Readme
@polygonlabs/bpn-rest-api
TypeScript client, Zod schemas, and OpenAPI spec for the BPN (Borderless Payment Network) REST API.
Installation
npm install @polygonlabs/bpn-rest-api
# or
pnpm add @polygonlabs/bpn-rest-apiZod schemas and TanStack Query hooks are optional — install the relevant peer dependencies only if you use those exports:
pnpm add zod # for ./zod
pnpm add @tanstack/react-query # for ./queryExports
| Import path | Contents |
|---|---|
| @polygonlabs/bpn-rest-api | TypeScript types, SDK functions, and default client singleton |
| @polygonlabs/bpn-rest-api/factory | createClient and related types — for advanced use cases (see below) |
| @polygonlabs/bpn-rest-api/query | TanStack Query v5 options factories and query keys (requires @tanstack/react-query) |
| @polygonlabs/bpn-rest-api/zod | Zod schemas for every request/response model (requires zod) |
| @polygonlabs/bpn-rest-api/openapi.json | Raw OpenAPI 3.0 spec |
Usage
Configure and authenticate
The package exports a pre-built client singleton. Configure it once at startup — all SDK
functions use it by default so you never need to pass it explicitly.
import { client, authorize } from '@polygonlabs/bpn-rest-api';
// Point at your BPN instance
client.setConfig({ baseUrl: 'https://api.example.com/api/v1' });
// authorize wraps the generated SDK function — on success it automatically calls
// client.setConfig({ headers: { Authorization: 'Bearer <token>' } }) so you don't have to.
const { data } = await authorize({ body: { username: 'my-partner-id', password: 'my-secret' } });
// In never-throw mode both `data` and `error` are optional — check `data` to confirm success.
if (!data) throw new Error('Auth failed');Call API methods
With the singleton configured, all SDK functions work without passing client:
import { listCorridors, createCustomer } from '@polygonlabs/bpn-rest-api';
const { data: corridors } = await listCorridors();
const { data: customer } = await createCustomer({
body: {
email: '[email protected]',
fullName: { firstName: 'Alice', lastName: 'Smith' },
address: {
line1: '123 Main St',
city: 'New York',
state: 'NY',
postalCode: '10001',
country: 'US',
},
phoneNumber: { countryCode: '+1', number: '5550100' },
dateOfBirth: '1990-01-15',
ssn: '123-45-6789',
},
});TanStack Query
The ./query export provides queryOptions and infiniteQueryOptions factories for every
endpoint, plus query key helpers for cache invalidation. List endpoints with cursor
pagination (startingAfter/endingBefore) get both standard and infinite variants.
import { useQuery, useInfiniteQuery } from '@tanstack/react-query';
import { listCustomersOptions, listCustomersInfiniteOptions } from '@polygonlabs/bpn-rest-api/query';
// Standard query — fetches one page
function CustomerList() {
const { data, error, isPending } = useQuery(listCustomersOptions());
if (isPending) return <span>Loading…</span>;
if (error) return <span>Error: {error.message}</span>;
return <ul>{data?.data?.map(c => <li key={c.id}>{c.email}</li>)}</ul>;
}
// Infinite query — cursor-based pagination
function AllCustomers() {
const { data, fetchNextPage, hasNextPage } = useInfiniteQuery({
...listCustomersInfiniteOptions(),
getNextPageParam: (lastPage) => lastPage.data?.at(-1)?.id,
initialPageParam: undefined,
});
return (
<>
{data?.pages.flatMap(p => p.data ?? []).map(c => (
<div key={c.id}>{c.email}</div>
))}
{hasNextPage && <button onClick={() => fetchNextPage()}>Load more</button>}
</>
);
}Validate responses with Zod
Zod schemas are exported with a z prefix matching each type name:
import { zCustomer } from '@polygonlabs/bpn-rest-api/zod';
const result = zCustomer.safeParse(customer);
if (!result.success) {
console.error(result.error.issues);
}Use the raw OpenAPI spec
import spec from '@polygonlabs/bpn-rest-api/openapi.json' with { type: 'json' };
console.log(spec.info.version);Error handling
The client supports two error handling styles. The default is the never-throw pattern.
Default: { data, error } (never-throw)
Every SDK function returns { data, error } and never throws, even on network failures or
non-2xx responses. This is the hey-api default — it makes the error path explicit at every
call site and avoids uncaught promise rejections when an await is not wrapped in
try/catch.
const { data: corridors, error } = await listCorridors();
if (error) {
// handle error — data is undefined
} else {
// use corridors — error is undefined
}Alternative: throwOnError (conventional async/await)
Set throwOnError: true via client.setConfig() and SDK functions throw on error instead
of returning { error }. Use this if you prefer centralised error handling via try/catch
rather than checking error at every call site.
client.setConfig({ throwOnError: true });
try {
const { data: corridors } = await listCorridors();
// data is CorridorList — error is never present, an error would have thrown instead
} catch (err) {
// handle error
}throwOnError can also be set per-call to override the client default.
Advanced: multiple client instances
The singleton covers most use cases. If you need isolated client instances — for example,
in a server-side context where each request carries a different auth token — use
createClient from the ./factory export:
import { createClient } from '@polygonlabs/bpn-rest-api/factory';
import { listCorridors } from '@polygonlabs/bpn-rest-api';
const requestClient = createClient({
baseUrl: 'https://api.example.com/api/v1',
headers: { Authorization: `Bearer ${token}` },
});
const { data } = await listCorridors({ client: requestClient });License
MIT
