Getting Started
First API Call
Make your first type-safe API call with Spoosh
This guide walks through making different types of API calls with Spoosh.
Reading Data
Use useRead to fetch data:
import { useRead } from "../api/client";
function UserProfile({ userId }: { userId: number }) {
const { data, loading, error } = useRead(
(api) => api.users[userId].$get()
);
if (loading) return <Spinner />;
if (error) return <ErrorMessage error={error} />;
return (
<div>
<h1>{data?.name}</h1>
<p>{data?.email}</p>
</div>
);
}With Query Parameters
function SearchUsers({ query }: { query: string }) {
const { data } = useRead(
(api) => api.search.$get({ query: { q: query, page: 1 } })
);
return (
<ul>
{data?.map((user) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}Conditional Fetching
function UserProfile({ userId }: { userId: number | null }) {
const { data, loading } = useRead(
(api) => api.users[userId!].$get(),
{ enabled: userId !== null }
);
// ...
}Writing Data
Use useWrite for mutations (POST, PUT, DELETE):
import { useWrite } from "../api/client";
function CreateUserForm() {
const { trigger, loading, error } = useWrite(
(api) => api.users.$post
);
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
const formData = new FormData(e.currentTarget);
const result = await trigger({
body: {
name: formData.get("name") as string,
email: formData.get("email") as string,
},
});
if (result.data) {
console.log("Created user:", result.data);
}
};
return (
<form onSubmit={handleSubmit}>
<input name="name" placeholder="Name" required />
<input name="email" type="email" placeholder="Email" required />
<button type="submit" disabled={loading}>
{loading ? "Creating..." : "Create User"}
</button>
{error && <p className="error">{error.message}</p>}
</form>
);
}Update and Delete
function UserActions({ userId }: { userId: number }) {
const { trigger: updateUser } = useWrite(
(api) => api.users[userId].$put
);
const { trigger: deleteUser } = useWrite(
(api) => api.users[userId].$delete
);
const handleUpdate = async () => {
await updateUser({ body: { name: "Updated Name" } });
};
const handleDelete = async () => {
await deleteUser();
};
return (
<div>
<button onClick={handleUpdate}>Update</button>
<button onClick={handleDelete}>Delete</button>
</div>
);
}Dynamic Path Parameters
Spoosh supports multiple syntaxes for dynamic path segments:
// Bracket syntax with literal value
const { data } = useRead((api) => api.users[123].$get());
// Function syntax with typed params (recommended)
const { data } = useRead(
(api) => api.users(":userId").$get({ params: { userId: "123" } })
);
// Variable in brackets
const userId = 123;
const { data } = useRead((api) => api.users[userId].$get());The function syntax (":userId") provides the best type inference for params.
Response Format
All API calls return a SpooshResponse:
type SpooshResponse<TData, TError> = {
status: number; // HTTP status code
data: TData | undefined; // Response data (if successful)
error: TError | undefined; // Error data (if failed)
headers?: Headers; // Response headers
aborted?: boolean; // True if request was aborted
};