Skip to content

With hey-api

hey-api generates typed clients from OpenAPI specs. It supports both fetch and axios backends — use the adapter to normalize either.

lib/expect-status.ts
import { createExpectStatus, adapters } from "expect-status";
export const expectStatus = createExpectStatus({
adapter: adapters.openapiClient,
fallbackMessage: "Request failed.",
defaults: {
401: "Please sign in.",
"5xx": "Service unavailable.",
},
});

Both client-fetch and client-axios return { data, error, response }. The built-in adapters.openapiClient preset handles both.

import { expectStatus } from "@/lib/expect-status";
import { getUser, createOrg } from "@/api/sdk";
const user = await expectStatus(200, getUser({ path: { id: "1" } }));
const org = await expectStatus(201, createOrg({ body: { name: "Acme" } }));
const org = await expectStatus(201, createOrg({ body: { name: "Acme" } }), {
409: ({ message }) => ({ error: message }),
422: "Please check your input.",
"5xx": "Service unavailable.",
});
function useUser(id: string) {
return useQuery({
queryKey: ["user", id],
queryFn: () => expectStatus(200, getUser({ path: { id } })),
});
}
function useCreateOrg() {
return useMutation({
mutationFn: (data: { name: string }) =>
expectStatus(201, createOrg({ body: data }), {
409: "Organisation already exists.",
}),
});
}
const result = await expectStatus(200, getUser({ path: { id } }), {
throws: false,
});
if (result.ok) {
renderUser(result.data);
} else {
showError(result.error.message);
}

Minimal @hey-api/openapi-ts config:

import { defineConfig } from "@hey-api/openapi-ts";
export default defineConfig({
client: "@hey-api/client-fetch",
input: "./openapi.yaml",
output: {
path: "./src/api/sdk",
},
});
import { expectStatus } from "expect-status";
import { getUser } from "@/api/sdk";
const { data, error, response } = await getUser({ path: { id: "1" } });
const user = await expectStatus(200, {
status: response.status,
body: data ?? error,
});