FAQ & Troubleshooting
Common Questions
How do I handle query parameters?
Use a path function:
const search = get<{ query: string; page?: number }, User[]>((input) => {
const params = new URLSearchParams({ q: input.query });
if (input.page) params.set('page', input.page.toString());
return `/users/search?${params}`;
});How do I add authentication to all requests?
Use a global beforeRequest hook:
hooks: {
beforeRequest: async (url, init) => ({
url,
init: { ...init, headers: { ...init.headers, Authorization: `Bearer ${getToken()}` } },
}),
}What’s the difference between hooks and custom handlers?
- Hooks — Apply to many endpoints (global, group, or endpoint level). Best for cross-cutting concerns: auth, logging, retries, token refresh.
- Custom handlers — Apply to one endpoint, replacing the default JSON behavior. Best for: file uploads, Blob downloads, non-JSON responses, response transformation.
Can I use endpoint-fetcher in Node.js?
Yes — provide a fetch implementation:
import fetch from 'node-fetch';
const api = createApiClient(endpoints, {
baseUrl: process.env.API_URL,
fetch: fetch as any,
});Node 18+ has native fetch, so no polyfill is needed.
How do I cancel a request?
Attach an AbortSignal in a beforeRequest hook:
const controller = new AbortController();
hooks: {
beforeRequest: async (url, init) => ({
url,
init: { ...init, signal: controller.signal },
}),
}
// Later:
controller.abort();How do I use different base URLs for different groups of endpoints?
Create separate clients, or override the URL in a group hook:
// Option 1: separate clients
const v1Api = createApiClient(endpoints, { baseUrl: 'https://api.example.com/v1' });
const v2Api = createApiClient(endpoints, { baseUrl: 'https://api.example.com/v2' });
// Option 2: override in a hook
legacy: group({
hooks: {
beforeRequest: async (url, init) => ({
url: url.replace('api.example.com', 'legacy.example.com'),
init,
}),
},
endpoints: { /* ... */ },
}),Troubleshooting
TypeScript error: “Property does not exist on type” — Your type definition is missing a property that the API returns. Add it to the type, or use a custom handler to transform the response.
“Failed to fetch” / network error — Check CORS headers on your API server, verify baseUrl is correct, and ensure there are no firewall or proxy issues.
“Unexpected token < in JSON at position 0” — The server returned HTML instead of JSON (often a 404 page). Use a custom handler to check response.ok before calling response.json().
Infinite loop / maximum call stack exceeded — A hook is triggering itself recursively. Guard against it by checking the URL:
afterResponse: async (response, url, init) => {
if (response.status === 401 && !url.includes('/auth/refresh')) {
// safe to call refresh here
}
return response;
},Plugin hooks not firing — Make sure the plugin is in the plugins array: plugins: [myPlugin()]. Defining a plugin without adding it has no effect.
Autocomplete not working — Make sure you’ve provided explicit type parameters: get<{ id: string }, User>(...) rather than get(...).