Schema Definition
Define type-safe API schemas with TypeScript
Spoosh uses TypeScript types to define your API structure. This gives you full autocomplete and type checking for all API calls.
Basic Schema
Define your API schema as a flat path-based type structure:
type User = {
id: number;
name: string;
email: string;
};
type ApiSchema = {
users: {
GET: { data: User[] };
POST: { data: User; body: { name: string; email: string } };
};
"users/:id": {
GET: { data: User };
PUT: { data: User; body: Partial<User> };
DELETE: void;
};
};Endpoint Types
{ data }
For endpoints that only return data:
const { } = await ("users").GET();{ data; body }
Endpoint with a request body:
const { } = await ("users").POST({
: { : "John", : "john@example.com" },
});{ data; query }
Endpoint with query parameters:
const { } = await ("users").GET({
: { : 1, : 10 },
});{ data; body } with Files
Use the form() wrapper to send file uploads as multipart/form-data:
import { } from "@spoosh/core";
const = <>({ : "/api" });
const { } = await ("users/avatar").POST({
: ({ : }),
});Dynamic Path Segments
Use path parameters with :param syntax in your schema:
type ApiSchema = {
users: {
GET: { data: User[] };
};
"users/:id": {
GET: { data: User };
PUT: { data: User; body: Partial<User> };
DELETE: void;
};
};
// Usage with params
const { data: users } = await api("users").GET(); // GET /users
const { data: user } = await api("users/:id").GET({ params: { id: 123 } }); // GET /users/123
const { data } = await api("users/:id").PUT({ params: { id: 123 }, body: {} }); // PUT /users/123
await api("users/:id").DELETE({ params: { id: 123 } }); // DELETE /users/123
// With variables
const userId = 123;
await api("users/:id").GET({ params: { id: userId } });Nested Dynamic Segments
type ApiSchema = {
"users/:userId/posts": {
GET: { data: Post[] };
};
"users/:userId/posts/:postId": {
GET: { data: Post };
};
};
// Usage
await api("users/:userId/posts").GET({ params: { userId: 1 } }); // GET /users/1/posts
await api("users/:userId/posts/:postId").GET({
params: { userId: 1, postId: 42 },
}); // GET /users/1/posts/42With React Hooks
Dynamic params work seamlessly with useRead and useWrite:
import { useRead, useWrite } from "./api/client";
function UserPostsList({ userId }: { userId: number }) {
// useRead with params
const { data: posts } = useRead((api) =>
api("users/:userId/posts").GET({ params: { userId } })
);
// useWrite with POST
const { trigger: createPost } = useWrite(
(api) => api("users/:userId/posts").POST
);
// useWrite with DELETE and params
const { trigger: deletePost } = useWrite(
(api) => api("users/:userId/posts/:postId").DELETE
);
const handleCreatePost = async () => {
await createPost({ params: { userId }, body: { title: "New Post" } });
};
const handleDeletePost = async (postId: number) => {
await deletePost({ params: { userId, postId } });
};
return (
<div>
<button onClick={handleCreatePost}>Create Post</button>
{posts?.map((post) => (
<div key={post.id}>
<h3>{post.title}</h3>
<button onClick={() => handleDeletePost(post.id)}>Delete</button>
</div>
))}
</div>
);
}HTTP Methods
Spoosh supports all common HTTP methods:
| Method | Description |
|---|---|
GET | GET request |
POST | POST request |
PUT | PUT request |
PATCH | PATCH request |
DELETE | DELETE request |
Schema Organization
For larger APIs, organize your schema into separate files:
export type UsersSchema = {
users: {
GET: { data: User[]; query: { page?: number } };
POST: { data: User; body: CreateUserBody };
};
"users/:id": {
GET: { data: User };
PUT: { data: User; body: UpdateUserBody };
DELETE: void;
};
};export type PostsSchema = {
posts: {
GET: { data: Post[] };
POST: { data: Post; body: CreatePostBody };
};
};import type { UsersSchema } from "./users";
import type { PostsSchema } from "./posts";
export type ApiSchema = UsersSchema & PostsSchema;Type Inference from Server Frameworks
Hono
If you're using Hono on the server, you can automatically infer your schema:
import type { HonoToSpoosh } from "@spoosh/hono";
import type { hc } from "hono/client";
import type { AppType } from "./server";
type ApiSchema = HonoToSpoosh<ReturnType<typeof hc<AppType>>>;See the Hono guide for details.
Elysia
If you're using Elysia on the server, you can automatically infer your schema:
import type { ElysiaToSpoosh } from "@spoosh/elysia";
import type { App } from "./server";
type ApiSchema = ElysiaToSpoosh<App>;See the Elysia guide for details.
Summary
| Type | Description | Example |
|---|---|---|
{ data } | Endpoint with data only | GET: { data: User[] } |
{ data; body } | Endpoint with JSON body | POST: { data: User; body: CreateUserBody } |
{ data; query } | Endpoint with query params | GET: { data: User[]; query: { page: number } } |
{ data; error } | Endpoint with typed error | GET: { data: User; error: ApiError } |
void | No response body | DELETE: void |
"path/:param" | Dynamic path segment | "users/:id": { GET: { data: User } } |