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:
| Field | Type | Description |
|---|---|---|
status | number | HTTP status code |
data | TData | undefined | Response data (present on success) |
error | TError | undefined | Error object (present on failure) |
headers | Headers | undefined | Response headers |
aborted | boolean | undefined | True 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/:id").GET({ params: { id: 1 } })
);
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) => api("posts").POST);
const handleSubmit = async () => {
await trigger({ body: { 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 { trigger } = useWrite((api) => api("users").POST);
await trigger({ 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.