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 nested type structure:
import type { Endpoint } from "@spoosh/core";
type User = {
id: number;
name: string;
email: string;
};
type ApiSchema = {
users: {
$get: User[]; // Simple form - just the return type
$post: Endpoint<User, { name: string; email: string }>; // With body
};
};Endpoint Types
Simple Data Type
For endpoints that only return data (no body, query, or formData), you can use the type directly:
const { } = await .users.$get();Endpoint<TData>
For more explicit typing, use the Endpoint wrapper:
const { } = await .users.$get();Endpoint<TData, TBody>
Endpoint with a request body:
const { } = await .users.$post({
: { : "John", : "john@example.com" },
});EndpointWithQuery<TData, TQuery>
Endpoint with query parameters:
const { } = await .users.$get({
: { : 1, : 10 },
});EndpointWithFormData<TData, TFormData>
Endpoint that accepts form data:
const { } = await .users.avatar.$post({
: { : },
});EndpointDefinition<T>
For full control over all endpoint properties:
const { } = await .users.$get({
: { : 1, : 10 },
});Dynamic Path Segments
Use _ to define dynamic path segments:
type ApiSchema = {
users: {
$get: Endpoint<User[]>;
_: {
$get: Endpoint<User>;
$put: Endpoint<User, Partial<User>>;
$delete: Endpoint<void>;
};
};
};
// Usage
const { data: users } = await api.users.$get(); // GET /users
const { data: user } = await api.users[123].$get(); // GET /users/123
const { data } = await api.users[123].$put({ body: {} }); // PUT /users/123
await api.users[123].$delete(); // DELETE /users/123Nested Dynamic Segments
type ApiSchema = {
users: {
_: {
posts: {
$get: Endpoint<Post[]>;
_: {
$get: Endpoint<Post>;
};
};
};
};
};
// Usage
await api.users[1].posts.$get(); // GET /users/1/posts
await api.users[1].posts[42].$get(); // GET /users/1/posts/42Typed Dynamic Params
Use the function syntax for type-safe params:
// Function syntax (recommended for type safety)
const { data } = await api.users(":userId").$get({
params: { userId: "123" },
});
// Bracket syntax (works but no type inference)
const { data } = await api.users[userId].$get();HTTP Methods
Spoosh supports all common HTTP methods:
| Method | Type | Description |
|---|---|---|
$get | Endpoint | GET request |
$post | Endpoint | POST request |
$put | Endpoint | PUT request |
$patch | Endpoint | PATCH request |
$delete | Endpoint | DELETE request |
Schema Organization
For larger APIs, organize your schema into separate files:
import type { Endpoint, EndpointWithQuery } from "@spoosh/core";
export type UsersSchema = {
users: {
$get: EndpointWithQuery<User[], { page?: number }>;
$post: Endpoint<User, CreateUserBody>;
_: {
$get: Endpoint<User>;
$put: Endpoint<User, UpdateUserBody>;
$delete: Endpoint<void>;
};
};
};import type { Endpoint } from "@spoosh/core";
export type PostsSchema = {
posts: {
$get: Endpoint<Post[]>;
$post: Endpoint<Post, CreatePostBody>;
};
};import type { UsersSchema } from "./users";
import type { PostsSchema } from "./posts";
export type ApiSchema = UsersSchema & PostsSchema;Type Inference from Hono
If you're using Hono on the server, you can automatically infer your schema:
import type { HonoToSpoosh } from "@spoosh/hono";
import type { AppType } from "./server";
type ApiSchema = HonoToSpoosh<AppType>;See the Hono Integration guide for details.
Summary
| Type | Description | Example |
|---|---|---|
TData | Simple data type (no body) | $get: User[] |
Endpoint<TData> | Explicit endpoint | $get: Endpoint<User[]> |
Endpoint<TData, TBody> | Endpoint with JSON body | $post: Endpoint<User, CreateUserBody> |
EndpointWithQuery<TData, TQuery> | Endpoint with query params | $get: EndpointWithQuery<User[], { page: number }> |
EndpointWithFormData<TData, TForm> | Endpoint with form data | $post: EndpointWithFormData<Result, { file: File }> |
EndpointDefinition<T> | Full endpoint definition | $get: EndpointDefinition<{ data: User[]; query: { page: number } }> |
_ | Dynamic path segment | users: { _: { $get: User } } |