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.
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.
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>
);
}
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>
);
}
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>
);
}
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.
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);