Groups
Groups organize related endpoints under a shared namespace, with optional hooks and a basePath.
Basic Groups
import { createApiClient, group, get, post, del } from 'endpoint-fetcher';
const api = createApiClient({
users: group({
endpoints: {
list: get<void, User[]>('/users'),
getById: get<{ id: string }, User>((input) => `/users/${input.id}`),
create: post<CreateUserInput, User>('/users'),
delete: del<{ id: string }, void>((input) => `/users/${input.id}`),
},
}),
posts: group({
endpoints: {
list: get<void, Post[]>('/posts'),
getById: get<{ id: string }, Post>((input) => `/posts/${input.id}`),
},
}),
}, {
baseUrl: 'https://api.example.com',
});
await api.users.list();
await api.users.getById({ id: '123' });
await api.posts.list();Nested Groups
Groups can contain other groups via the groups key:
const api = createApiClient({
admin: group({
groups: {
users: group({
endpoints: {
list: get<void, User[]>('/admin/users'),
ban: post<{ id: string }, void>((input) => `/admin/users/${input.id}/ban`),
},
}),
reports: group({
endpoints: {
daily: get<{ date: string }, Report>((input) => `/admin/reports/${input.date}`),
},
}),
},
}),
}, { baseUrl: 'https://api.example.com' });
await api.admin.users.list();
await api.admin.users.ban({ id: '123' });
await api.admin.reports.daily({ date: '2024-01-01' });A group can have both endpoints (direct children) and groups (nested sub-groups) at the same time.
basePath
Set basePath on a group to prefix all endpoint paths within it. The prefix accumulates across nested groups:
const api = createApiClient({
users: group({
basePath: '/users',
endpoints: {
list: get<void, User[]>('/'), // → GET /users/
getById: get<{ id: string }, User>((i) => `/${i.id}`), // → GET /users/123
},
groups: {
posts: group({
basePath: '/posts',
endpoints: {
list: get<void, Post[]>('/'), // → GET /users/posts/
},
}),
},
}),
}, { baseUrl: 'https://api.example.com' });Group Hooks
Hooks defined on a group apply to all endpoints in it, including nested groups:
const api = createApiClient({
protected: group({
hooks: {
beforeRequest: async (url, init) => ({
url,
init: {
...init,
headers: { ...init.headers, Authorization: `Bearer ${getToken()}` },
},
}),
},
endpoints: {
me: get<void, User>('/me'),
settings: get<void, Settings>('/settings'),
},
}),
public: group({
endpoints: {
health: get<void, { status: string }>('/health'),
},
}),
}, { baseUrl: 'https://api.example.com' });
await api.protected.me(); // ✅ Authorization header automatically added
await api.public.health(); // no authSee Hooks for the full hook execution order across levels.