Démarrage Rapide
pnpm add @unireq/core @unireq/http @unireq/presetsLe plus simple : httpClient()
Pour du prototypage rapide ou des appels API simples, utilisez httpClient() de @unireq/presets :
import { httpClient } from '@unireq/presets';
// Créer un client avec des valeurs par défaut sensées
const api = httpClient('https://api.example.com');
const user = await api.get('/users/42');
// Avec options
const api = httpClient('https://api.example.com', {
timeout: 10000,
headers: { 'X-API-Key': 'secret' },
});
// Méthodes safe (retournent Result au lieu de lever des exceptions)
const result = await api.safe.get('/users/42');
if (result.isOk()) {
console.log(result.value.data);
} else {
console.error(result.error.message);
}Construire un client personnalisé
Unireq est fonctionnel et composable. Vous construisez un client en passant un transport (HTTP, FTP, IMAP…) et une liste de policies. Elles s'exécutent dans l'ordre donné à l'aller, puis reviennent en sens inverse au retour.
import { client } from '@unireq/core';
import { http, headers, parse } from '@unireq/http';
// Client avec base URL et policies globales
const api = client(
http('https://api.example.com'),
headers({ 'user-agent': 'unireq/1.0' }),
parse.json() // Automatically parse JSON responses
);
const response = await api.get('/users/123');
if (response.ok) {
console.log(response.data); // Typed response
} else {
console.error('Request failed:', response.status);
}Note : Contrairement à d'autres bibliothèques, Unireq ne lève pas d'erreur pour les réponses non-2xx par défaut. Vérifiez
response.ok/response.status, ou ajoutez une policy commethrowOnError()si vous préférez les exceptions.
Overrides ponctuels
Les policies globales gardent votre client DRY, mais vous pouvez en ajouter par requête :
import { body, parse } from '@unireq/http';
// API variadique (append policies)
await api.post('/users', body.json(payload), parse.json());
// API RequestOptions (plus explicite)
await api.post('/users', {
body: payload, // Automatiquement wrappé dans body.json()
policies: [customPolicy],
signal: abortController.signal,
});Les policies passées à la requête sont ajoutées après la pile du client, donc elles restent au plus près du transport. Idéal pour du parsing ad hoc, des retries conditionnels ou des en-têtes temporaires.
Gestion fonctionnelle des erreurs avec Result
Au lieu de try/catch, utilisez le type Result<T, E> pour une gestion fonctionnelle des erreurs :
import { ok, err, fromPromise, type Result } from '@unireq/core';
// Créer des résultats
const success: Result<number, Error> = ok(42);
const failure: Result<number, Error> = err(new Error('échec'));
// Transformer avec map/flatMap
const doubled = success.map(n => n * 2); // ok(84)
const chained = success.flatMap(n => ok(n + 1)); // ok(43)
// Extraire les valeurs en toute sécurité
success.unwrap(); // 42
failure.unwrapOr(0); // 0 (valeur par défaut)
// Pattern matching
const message = success.match({
ok: (value) => `Reçu ${value}`,
err: (error) => `Erreur: ${error.message}`,
});
// Type guards
if (success.isOk()) {
console.log(success.value); // TypeScript sait que c'est Ok
}
// Depuis des opérations asynchrones
const result = await fromPromise(fetch('/api'));Méthodes safe du client
Chaque client dispose d'un namespace safe qui retourne Result au lieu de lever des exceptions :
const api = client(http('https://api.example.com'), parse.json());
// API traditionnelle (lève des exceptions sur erreurs réseau)
try {
const res = await api.get('/users');
} catch (error) {
handleError(error);
}
// API safe (fonctionnelle)
const result = await api.safe.get<User[]>('/users');
if (result.isOk()) {
console.log(result.value.data);
} else {
console.error(result.error.message);
}
// Chaîner les opérations
const names = await api.safe.get<User[]>('/users')
.then(r => r.map(res => res.data.map(u => u.name)));Client HTTPS avancé (OAuth, retries, négociation de contenu)
import { client, compose, either, retry, backoff } from '@unireq/core';
import { http, headers, parse, redirectPolicy, httpRetryPredicate, rateLimitDelay } from '@unireq/http';
import { oauthBearer } from '@unireq/oauth';
import { parse as xmlParse } from '@unireq/xml';
const smartClient = client(
http('https://api.example.com'),
headers({ accept: 'application/json, application/xml' }),
redirectPolicy({ allow: [307, 308] }),
retry(
httpRetryPredicate({ methods: ['GET', 'PUT', 'DELETE'], statusCodes: [429, 503] }),
[rateLimitDelay(), backoff({ initial: 100, max: 5000 })],
{ tries: 3 }
),
oauthBearer({ tokenSupplier: async () => getAccessToken() }),
either(
(ctx) => ctx.headers.accept?.includes('json'),
parse.json(),
xmlParse()
)
);
**Pourquoi cet ordre ?**
- `headers` / `redirectPolicy` enveloppent tout pour préparer la requête avant les couches de résilience.
- `retry` reste **à l'extérieur** d'`oauthBearer` pour que la couche OAuth voie les `401` et rafraîchisse le token avant qu'un retry général ne rejoue l'appel.
- Le parsing (`parse.json` / XML) doit être au plus proche du transport afin de voir la réponse finale, après toutes les tentatives.
const user = await smartClient.get('/users/me');Prochaines étapes
- Philosophie — Pourquoi Unireq diffère d'Axios/Fetch.
- Composition — Rappel complet sur l'ordre des policies.
- Architecture — Structure des packages et couches.
- Exemples — Scripts prêts à l'emploi alignés sur ce guide.