Source: https://www.hype-stack.dev/docs/frontend/http

# 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`:

```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:

```tsx
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:

```tsx
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:

```tsx
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:

```ts
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:

```tsx
const request = sdk.projects.$projectId.$get.setParams({ projectId: "abc" });
const { data } = useFetch(request);
```

For query params:

```tsx
const request = sdk.notifications.$get.setQueryParams({ limit: "5", status: "unread" });
const { data } = useFetch(request);
```
