Working with HTTP
All API calls go through the HyperFetch SDK. The SDK is typed end-to-end from Hono backend routes, so you get autocomplete and compile-time checks for every request.
Setup
The SDK is defined in src/api/sdk.ts:
import { createSdk } from "@hyper-fetch/core";
import type { ApiRoutesSdk } from "@hype-stack/backend";
import { client } from "./client";
export const sdk = createSdk<typeof client, ApiRoutesSdk>(client);The ApiRoutesSdk type is generated from the backend's Hono routes. New endpoints are available immediately on the SDK
with no manual wiring.
Reading data
Use useFetch for GET requests. It fetches on mount and caches the response:
import { useFetch } from "@hyper-fetch/react";
import { sdk } from "@/api/sdk";
function ProjectList() {
const { data, loading, error, refetch } = useFetch(sdk.projects.$get);
if (loading) return <Spinner />;
if (error) return <ErrorMessage error={error} />;
return (
<ul>
{data?.items.map((project) => (
<li key={project.id}>{project.name}</li>
))}
</ul>
);
}Mutations
Use useSubmit for POST, PUT, PATCH, DELETE. Pass the request body under payload, and read the result from the
destructured tuple submit returns:
import { useSubmit } from "@hyper-fetch/react";
import { sdk } from "@/api/sdk";
function CreateProject() {
const { submit, submitting } = useSubmit(sdk.projects.$post);
const handleCreate = async () => {
const { error } = await submit({ payload: { name: "New project" } });
if (error) {
toast.error("Failed to create project");
return;
}
toast.success("Project created");
};
return <button onClick={handleCreate}>{submitting ? "Creating..." : "Create"}</button>;
}Side-effect callbacks
useSubmit returns onSubmitSuccess and onSubmitError functions. Register them at the top level of your component,
they aren't options on the hook. The success callback receives { response }, where response.data holds the typed
payload:
function CreateProject() {
const { submit, submitting, onSubmitSuccess, onSubmitError } = useSubmit(sdk.projects.$post);
onSubmitSuccess(({ response }) => {
toast.success(`Created ${response.data.name}`);
});
onSubmitError(() => {
toast.error("Failed to create project");
});
return (
<button onClick={() => submit({ payload: { name: "New project" } })}>
{submitting ? "Creating..." : "Create"}
</button>
);
}Cache invalidation
When a mutation affects data shown elsewhere, invalidate the cache so all useFetch hooks refetch:
import { client } from "@/api/client";
client.cache.invalidate(/\/projects/);Always import client from @/api/client directly. Don't reach through an SDK request to access the cache.
Query params and path params
Set query params or path params on the request before passing it to a hook:
const request = sdk.projects.$projectId.$get.setParams({ projectId: "abc" });
const { data } = useFetch(request);For query params:
const request = sdk.notifications.$get.setQueryParams({ limit: "5", status: "unread" });
const { data } = useFetch(request);Every purchase and sponsorship funds my 8+ years of work on open source given freely to the community. It keeps the lights on, funds new packs, and keeps the ecosystem alive. Even a small tier means a lot. Thank you!
