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 injectRead to fetch data:
import { Component, input } from "@angular/core";
import { injectRead } from "../api/client";
@Component({
selector: "app-user-profile",
template: `
@if (user.loading()) {
<app-spinner />
} @else if (user.error()) {
<app-error-message [error]="user.error()!" />
} @else {
<div>
<h1>{{ user.data()?.name }}</h1>
<p>{{ user.data()?.email }}</p>
</div>
}
`,
})
export class UserProfileComponent {
userId = input.required<number>();
user = injectRead((api) =>
api("users/:id").GET({ params: { id: this.userId() } })
);
}With Query Parameters
import { Component, input } from "@angular/core";
import { injectRead } from "../api/client";
@Component({
selector: "app-search-users",
template: `
<ul>
@for (user of searchResults.data(); track user.id) {
<li>{{ user.name }}</li>
}
</ul>
`,
})
export class SearchUsersComponent {
searchTerm = input.required<string>();
searchResults = injectRead((api) =>
api("search").GET({ query: { q: this.searchTerm(), page: 1 } })
);
}Conditional Fetching
import { Component, input } from "@angular/core";
import { injectRead } from "../api/client";
@Component({
selector: "app-user-profile",
template: `...`,
})
export class UserProfileComponent {
userId = input<number | null>(null);
user = injectRead(
(api) => api("users/:id").GET({ params: { id: this.userId()! } }),
{
enabled: () => this.userId() !== null,
}
);
}Writing Data
Use injectWrite for mutations (POST, PUT, DELETE):
import { Component } from "@angular/core";
import { injectWrite } from "../api/client";
@Component({
selector: "app-create-user-form",
template: `
<form (ngSubmit)="handleSubmit($event)">
<input name="name" placeholder="Name" required #nameInput />
<input
name="email"
type="email"
placeholder="Email"
required
#emailInput
/>
<button type="submit" [disabled]="createUser.loading()">
{{ createUser.loading() ? "Creating..." : "Create User" }}
</button>
@if (createUser.error()) {
<p class="error">{{ createUser.error()!.message }}</p>
}
</form>
`,
})
export class CreateUserFormComponent {
createUser = injectWrite((api) => api("users").POST);
async handleSubmit(event: SubmitEvent) {
event.preventDefault();
const form = event.target as HTMLFormElement;
const formData = new FormData(form);
const result = await this.createUser.trigger({
body: {
name: formData.get("name") as string,
email: formData.get("email") as string,
},
});
if (result.data) {
console.log("Created user:", result.data);
}
}
}Update and Delete
import { Component, input } from "@angular/core";
import { injectWrite } from "../api/client";
@Component({
selector: "app-user-actions",
template: `
<div>
<button (click)="handleUpdate()">Update</button>
<button (click)="handleDelete()">Delete</button>
</div>
`,
})
export class UserActionsComponent {
userId = input.required<number>();
updateUser = injectWrite((api) => api("users/:id").PUT);
deleteUser = injectWrite((api) => api("users/:id").DELETE);
async handleUpdate() {
await this.updateUser.trigger({
params: { id: this.userId() },
body: { name: "Updated Name" },
});
}
async handleDelete() {
await this.deleteUser.trigger({ params: { id: this.userId() } });
}
}Dynamic Path Parameters
Spoosh supports dynamic path segments with path-based syntax:
// injectRead: With params object
user = injectRead((api) => api("users/:id").GET({ params: { id: 123 } }));
// injectRead: With reactive values
userId = input.required<number>();
user = injectRead((api) =>
api("users/:id").GET({ params: { id: this.userId() } })
);
// injectWrite: Pass params when triggering
userId = input.required<number>();
updateUser = injectWrite((api) => api("users/:id").PUT);
// Usage: await this.updateUser.trigger({ params: { id: this.userId() }, body: { name: "New Name" } });
// Multiple params
deletePost = injectWrite((api) => api("users/:userId/posts/:postId").DELETE);
// Usage: await this.deletePost.trigger({ params: { userId: 1, postId: 42 } });Recommended: Use the path-based syntax api("users/:id").GET({ params: { id } }) for clear and type-safe code.
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
};