Spoosh
Core

Response Format

Understanding the SpooshResponse structure

All Spoosh API calls return a consistent SpooshResponse object. This makes error handling predictable across your application.

Response Structure

type SpooshResponse<TData, TError> =
  | { status: number; data: TData; error?: undefined; headers?: Headers; aborted?: false }
  | { status: number; data?: undefined; error: TError; headers?: Headers; aborted?: boolean };

Every response includes:

FieldTypeDescription
statusnumberHTTP status code
dataTData | undefinedResponse data (present on success)
errorTError | undefinedError object (present on failure)
headersHeaders | undefinedResponse headers
abortedboolean | undefinedTrue if request was aborted

Handling Responses

The hooks automatically parse the response and provide separate data and error fields:

function UserProfile() {
  const { data, error, loading } = useRead((api) => api.users[1].$get());

  if (loading) return <Spinner />;
  if (error) return <Error message={error.message} />;

  return <div>{data.name}</div>;
}

Aborting Requests

All hooks return an abort function to cancel in-flight requests:

function SearchUsers() {
  const { data, loading, abort } = useRead(
    (api) => api.search.$get({ query: { q: searchTerm } }),
    { enabled: searchTerm.length > 0 }
  );

  return (
    <div>
      <input
        value={searchTerm}
        onChange={(e) => setSearchTerm(e.target.value)}
      />
      {loading && <button onClick={abort}>Cancel</button>}
      {data?.map((user) => <UserCard key={user.id} user={user} />)}
    </div>
  );
}

useWrite Abort

function CreatePost() {
  const { trigger, loading, abort } = useWrite(
    (api, data) => api.posts.$post({ body: data })
  );

  const handleSubmit = async () => {
    await trigger({ title: "New Post", content: "..." });
  };

  return (
    <div>
      <button onClick={handleSubmit} disabled={loading}>Submit</button>
      {loading && <button onClick={abort}>Cancel</button>}
    </div>
  );
}

useInfiniteRead Abort

function PostFeed() {
  const { data, loading, abort, fetchNext } = useInfiniteRead(
    (api, { pageParam }) => api.posts.$get({ query: { cursor: pageParam } }),
    { getNextPageParam: (lastPage) => lastPage.nextCursor }
  );

  return (
    <div>
      {loading && <button onClick={abort}>Cancel Loading</button>}
      {/* ... */}
    </div>
  );
}

Input Echo

The response includes the input that was sent with the request:

const { data } = useRead((api) =>
  api.users.$post({ body: { name: "John", email: "john@example.com" } })
);

// response.input?.body contains { name: "John", email: "john@example.com" }

This is useful for optimistic updates and debugging.

On this page