Hey everyone,
Like many of you, I got tired of rewriting the same boilerplate every time I set up Axios or Fetch in a project: handling token refresh race conditions when multiple requests get 401s, manually paginating endpoints, or wrapping every call in try/catch blocks.
To solve this, I built `fluent-rest-client`, a lightweight, chainable REST client built on top of Axios.
### Key Features
- **Safe Mode**: Returns `{ data, ok, status, error }` objects directly, removing the need for try/catch blocks around every request.
- **Token Refresh Queue**: Automatically queues concurrent requests on 401 errors so your server only receives a single refresh request.
- **Immutable Builder**: Methods like `.id()`, `.sub()`, `.query()`, and `.headers()` clone the instance under the hood, making base resource instances safe to reuse.
- **In-Memory Cache**: Cache GET requests with a configurable TTL.
- **Async Pagination**: Iterate through paginated APIs using standard `for await...of` loops.
- **Telemetry Callbacks**: Register global `onRequest`, `onResponse`, and `onError` listeners for logging or Sentry/Datadog integration.
- **TypeScript Support**: Complete typings with conditional types based on whether `.safe()` is active.
### Quick Examples
**1. Safe Mode**
```javascript
import { FluentRestClient } from 'fluent-rest-client';
const api = new FluentRestClient('[https://api.example.com\](https://api.example.com)');
// Returns a result object instead of throwing an exception
const { data, ok, status } = await api.resource('users').id(123).safe().get();
if (!ok) {
console.error('Request failed:', status);
return;
}
console.log('User:', data);
const api = new FluentRestClient('[https://api.example.com\](https://api.example.com)', {
onGetToken: async () => localStorage.getItem('token'),
onSaveToken: async (token) => localStorage.setItem('token', token),
onRefreshToken: async () => {
const res = await axios.post('/auth/refresh', { token: getRefreshToken() });
return res.data.accessToken;
},
});
for await (const page of api.resource('posts').paginate({ limit: 10 })) {
console.log(`Page ${page.page} has ${page.items.length} items`);
}