Skip to Content
✨ New: Auth Plugin. JWT, OAuth2, API Key, and more authentication strategies out of the box. Read more... 🎉
Examples

Examples

Real-world examples combining multiple features.

Full REST API Client

A production-ready client with authentication, token refresh, grouped endpoints, and error tracking:

import { createApiClient, group, get, post, patch, del } from 'endpoint-fetcher'; type User = { id: string; name: string; email: string; role: 'admin' | 'user' }; type Post = { id: string; title: string; content: string; userId: string }; type ApiError = { message: string; code: string }; const getToken = () => localStorage.getItem('jwt'); const setToken = (t: string) => localStorage.setItem('jwt', t); const api = createApiClient({ auth: group({ endpoints: { login: post<{ email: string; password: string }, { token: string; user: User }, ApiError>('/auth/login'), logout: post<void, void>('/auth/logout'), refresh: post<void, { token: string }>('/auth/refresh'), }, }), users: group({ endpoints: { me: get<void, User, ApiError>('/users/me'), updateMe: patch<Partial<User>, User, ApiError>('/users/me'), }, groups: { posts: group({ endpoints: { list: get<void, Post[]>('/users/me/posts'), create: post<Omit<Post, 'id' | 'userId'>, Post, ApiError>('/users/me/posts'), delete: del<{ id: string }, void, ApiError>((input) => `/users/me/posts/${input.id}`), }, }), }, }), admin: group({ hooks: { beforeRequest: async (url, init) => ({ url, init: { ...init, headers: { ...init.headers, 'X-Admin-Request': 'true' } }, }), }, groups: { users: group({ endpoints: { list: get<{ page?: number }, { data: User[]; total: number }>((input) => { const params = new URLSearchParams(); if (input?.page) params.set('page', input.page.toString()); return `/admin/users?${params}`; }), ban: post<{ id: string; reason: string }, void, ApiError>((input) => `/admin/users/${input.id}/ban`), }, }), }, }), }, { baseUrl: import.meta.env.VITE_API_URL || 'https://api.example.com', defaultHeaders: { 'Content-Type': 'application/json' }, hooks: { beforeRequest: async (url, init) => { const token = getToken(); if (token && !url.includes('/auth/login')) { return { url, init: { ...init, headers: { ...init.headers, Authorization: `Bearer ${token}` } } }; } return { url, init }; }, afterResponse: async (response, url, init) => { if (response.status === 401 && !url.includes('/auth/')) { try { const { token } = await api.auth.refresh(); setToken(token); return fetch(url, { ...init, headers: { ...init.headers, Authorization: `Bearer ${token}` } }); } catch { window.location.href = '/login'; throw new Error('Session expired'); } } return response; }, onError: async (error) => { console.error('API Error:', error); }, }, }); export default api;

React Query Integration

import { createApiClient, group, get, post, patch, del } from 'endpoint-fetcher'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; 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'), update: patch<UpdateUserInput, User>((input) => `/users/${input.id}`), delete: del<{ id: string }, void>((input) => `/users/${input.id}`), }, }), }, { baseUrl: 'https://api.example.com' }); // Query key factory const userKeys = { all: () => ['users'] as const, detail: (id: string) => ['users', id] as const, }; export function useUsers() { return useQuery({ queryKey: userKeys.all(), queryFn: () => api.users.list() }); } export function useUser(id: string) { return useQuery({ queryKey: userKeys.detail(id), queryFn: () => api.users.getById({ id }) }); } export function useCreateUser() { const qc = useQueryClient(); return useMutation({ mutationFn: (data: CreateUserInput) => api.users.create(data), onSuccess: () => qc.invalidateQueries({ queryKey: userKeys.all() }), }); }

Testing with a Mock Fetch

const mockFetch: typeof fetch = async (input, init) => { const url = typeof input === 'string' ? input : input.toString(); if (url.includes('/users') && init?.method === 'GET') { return new Response( JSON.stringify([{ id: '1', name: 'Test User', email: 'test@example.com' }]), { status: 200, headers: { 'Content-Type': 'application/json' } } ); } return new Response(JSON.stringify({ message: 'Not found' }), { status: 404 }); }; const api = createApiClient({ users: group({ endpoints: { list: get<void, User[]>('/users') } }), }, { baseUrl: 'https://api.example.com', fetch: mockFetch, }); const users = await api.users.list(); // users → [{ id: '1', name: 'Test User', ... }]