Client Http
@duplojs/http-client
est une librairie permettant de crĂ©er des clients HTTP typĂ©s. Elle a Ă©tĂ© conçue pour ĂȘtre utilisĂ©e avec @duplojs/types-codegen
qui génÚre les types à partir des routes de votre application DuploJS. Vous pouvez consulter la documentation de @duplojs/types-codegen
ici pour plus dâinformations.
- Installation
- Exemple minimal
- Pourquoi @duplojs/http-client ?
- CrĂ©ation de lâinstance du client HTTP
- Le concept dâinformation dans les rĂ©ponses HTTP
- RequĂȘtes POST
- MĂ©thodes de lâinstance du client HTTP
- ParamĂštres par dĂ©faut de la requĂȘte
- Intercepteurs
- Hooks
Installation
Pour utiliser @duplojs/http-client
, vous devez lâinstaller en tant que dĂ©pendance de votre projet.
npm install @duplojs/http-client
Exemple minimal
import { HttpClient } from '@duplojs/http-client';
const httpClient = new HttpClient({
baseUrl: "/"
});
const promiseRequest = httpClient
.get(
"/users/{userId}",
{
params: {
userId: "mySuperUserId",
},
},
)
promiseRequest
.whenResponseSuccess((response) => {
console.log(response)
})
.whenError((error) => {
console.error(error)
});
const response = await promiseRequest
const successResponse = await promiseRequest.iWantResponseSuccess();
Dans cet exemple :
- Un client http a étais créer avec la baseUrl
/
.- Nous utilisons la méthode
get
du client http pour effectuer une requĂȘte HTTP GET, cette mĂ©thode renvois unPromise
.- La requĂȘte est faite sur le path
/users/{userId}
, la valeur{userId}
du path sera remplacer pars la valeur définit aparams.userId
.- La méthode
whenResponseSuccess
dePromiseRequest
est utilisĂ©e pour dĂ©finir un callback qui sera executer dans le cas ou la requĂȘte porte un code200
.- La méthode
whenError
dePromiseRequest
est utilisĂ©e pour dĂ©finir un callback qui sera executer en cas de dâechec de la requĂȘte.- La
PromiseRequest
estawait
pour obtenir la réponse.- La méthode
iWantResponseSuccess
dePromiseRequest
renvois unPromise
qui rĂ©ussis uniquement si la requĂȘte porte un code200
.
Pourquoi @duplojs/http-client ?
Bien que des librairies comme fetch
ou axios
soient largement utilisĂ©es pour les requĂȘtes HTTP, @duplojs/http-client
apporte plusieurs avantages significatifs :
- Support des réponse avec information.
- Systéme de hook complet.
- Typage bout en bout possible
CrĂ©ation de lâinstance du client HTTP
Pour créer une nouvelle instance du client HTTP, utilisez la classe HttpClient
. Celle-ci accepte un objet de configuration avec les propriétés suivantes :
import { HttpClient } from '@duplojs/http-client';
const httpClient = new HttpClient({
baseUrl: "https://google.com/base/url",
keyToInformation: "my-info"
});
Dans cet exemple :
- Un objet
HttpClient
a Ă©tais instancier.- Le lien de lâAPI qui sera utilisĂ© pour faire des requĂȘte est
https://google.com/base/url
.- La clef a la quâelle sera chercher lâinformation dans les headers est
my-info
.
Propriété | Type | Valeur par défaut | Description |
---|---|---|---|
baseUrl | string | - | URL de base de lâAPI |
keyToInformation | string | information | clĂ© qui dĂ©signe lâinfo dans le header |
Le concept dâinformation dans les rĂ©ponses HTTP
Par dĂ©faut, HTTP utilise des codes de statut (200, 404, 500, etc.) pour indiquer le rĂ©sultat dâune requĂȘte. Cependant, ces codes sont souvent trop gĂ©nĂ©riques et manquent de contexte. Par exemple, un code 404 peut signifier :
- La route nâexiste pas
- La ressource demandĂ©e nâexiste pas
Pour résoudre ce problÚme, @duplojs/http-client
introduit le concept dâinformation. Une information est un identifiant unique envoyĂ© dans les en-tĂȘtes HTTP qui permet dâidentifier prĂ©cisĂ©ment le rĂ©sultat dâune requĂȘte.
await httpClient
.post(
"/timesheet",
{
body: {
...
},
},
)
.whenInformation("timesheet.created", () => {
// code par rapport Ă l'information timesheet.created
})
.whenInformation("workLocation.notfound", () => {
// code par rapport Ă l'information workLocation.notfound
})
.whenInformation("workingTime.exceeds13hWithoutBreak", () => {
// code par rapport Ă l'information workingTime.exceeds13hWithoutBreak
})
.whenInformation("workingTime.exceeds24Hours", () => {
// code par rapport Ă l'information workingTime.exceeds24Hours
});
Dans cet exemple, nous utilisons la méthode
whenInformation
pour exĂ©cuter du code en fonction de lâinformation retournĂ©e par la requĂȘte.
- Si lâinformation est
timesheet.created
, le premier callback sera exĂ©cutĂ©.- Si lâinformation est
workLocation.notfound
, le deuxiĂšme callback sera exĂ©cutĂ©.- Si lâinformation est
workingTime.exceeds13hWithoutBreak
, le troisiĂšme callback sera exĂ©cutĂ©.- Si lâinformation est
workingTime.exceeds24Hours
, le quatriÚme callback sera exécuté.
Les informations sont dĂ©finies par le serveur et peuvent ĂȘtre utilisĂ©es pour transmettre des messages dâerreur, des messages de succĂšs, des messages dâavertissement, etc. Cela Ă©vite dâavoir Ă analyser le corps de la rĂ©ponse pour dĂ©terminer le rĂ©sultat de la requĂȘte.
RequĂȘtes POST
Pour effectuer une requĂȘte POST, utilisez la mĂ©thode post
qui est disponible sur lâinstance du client HTTP.
httpClient
.post(
"/products",
{
body: {
name: "shoes",
price: 100
}
}
)
.whenResponseSuccess(() => {
console.log("Product created");
})
.whenError(() => {
console.log("Product not created");
});
Dans cet exemple :
- Nous utilisons la méthode
post
pour effectuer une requĂȘte HTTP POST.- La route
/products
est utilisĂ©e pour crĂ©er un nouveau produit.- Les donnĂ©es du produit sont dĂ©finies dans lâobjet
body
.- La méthode
whenResponseSuccess
est utilisée pour exécuter du code en cas de succÚs.- La méthode
whenError
est utilisĂ©e pour exĂ©cuter du code en cas dâerreur.
La fonctionnement des méthodes
put
,patch
etdelete
est similaire à celle de la méthodepost
.
MĂ©thodes de lâinstance du client HTTP
Une fois lâinstance du client HTTP créée, vous pouvez utiliser les mĂ©thodes suivantes :
Méthode | Description |
---|---|
setDefaultRequestParams | DĂ©finit les paramĂštres par dĂ©faut pour toutes les requĂȘtes |
setInterceptor | Configure les intercepteurs de requĂȘtes ou de rĂ©ponses |
request | Effectue une requĂȘte HTTP |
get | Effectue une requĂȘte HTTP GET |
post | Effectue une requĂȘte HTTP POST |
put | Effectue une requĂȘte HTTP PUT |
patch | Effectue une requĂȘte HTTP PATCH |
delete | Effectue une requĂȘte HTTP DELETE |
ParamĂštres par dĂ©faut de la requĂȘte
La méthode setDefaultRequestParams
permet de dĂ©finir des paramĂštres par dĂ©faut qui seront appliquĂ©s Ă toutes les requĂȘtes effectuĂ©es par le client HTTP. Ces paramĂštres peuvent ĂȘtre surchargĂ©s individuellement lors de lâappel des mĂ©thodes HTTP.
Utilisation basique
const httpClient = new HttpClient({
baseUrl: "https://api.example.com"
});
httpClient.setDefaultRequestParams({
headers: {
"Content-Type": "application/json",
get Authorization() {
return `Bearer ${localStorage.getItem("token")}`;
}
},
credentials: "include"
});
Dans cet exemple :
- Les en-tĂȘtes
Content-Type
etAuthorization
seront ajoutĂ©s Ă toutes les requĂȘtes.- Les credentials seront inclus dans toutes les requĂȘtes.
ParamĂštres disponibles
ParamĂštre | Type | Description |
---|---|---|
credentials | "include" | "same-origin" | "omit" | Gestion des credentials |
mode | "cors" | "no-cors" | "same-origin" | "navigate" | Mode CORS |
headers | Record<string, string> | En-tĂȘtes HTTP par dĂ©faut |
params | Record<string, string> | ParamĂštres dâURL par dĂ©faut |
query | Record<string, string> | ParamĂštres de requĂȘte par dĂ©faut |
redirect | "manual" | "follow" | "error" | Gestion des redirections |
referrer | "no-referrer" | "client" | Referrer |
referrerPolicy | "no-referrer" | "no-referrer-when-downgrade" | "origin" | "origin-when-cross-origin" | "same-origin" | "strict-origin" | "strict-origin-when-cross-origin" | "unsafe-url" | Politique de referrer |
integrity | string | Intégrité |
signal | AbortSignal | null | Signal dâannulation |
window | any | FenĂȘtre |
keepalive | boolean | Keepalive |
dispatcher | Dispatcher | undefined | Dispatcher |
duplex | "half" | undefined | Duplex |
Exemple complet
Pour montrer un cas dâutilisation complet, voici un exemple de configuration dâun client HTTP avec des paramĂštres par dĂ©faut ainsi que sont utilisation. Tout dâabord, nous crĂ©ons une instance du client HTTP et dĂ©finissons les paramĂštres par dĂ©faut :
export const httpClient = new HttpClient<HttpClientRoute>({
baseUrl: "api.example.com",
});
httpClient.setDefaultRequestParams({
headers: {
"Content-Type": "application/json",
"Accept-Language": "fr-FR"
},
mode: "cors",
query: {
"version": "1.0"
}
});
Dans cet exemple :
- LâURL de base de lâAPI est
api.example.com
.- Les en-tĂȘtes
Content-Type
etAccept-Language
seront ajoutĂ©s Ă toutes les requĂȘtes.- Le mode CORS est activĂ©.
- Le paramĂštre de requĂȘte
version
est dĂ©fini Ă1.0
.- Les autres paramÚtres sont laissés à leurs valeurs par défaut.
Ensuite nous utilisons le client HTTP pour effectuer une requĂȘte :
// Les paramÚtres par défaut sont appliqués automatiquement
const response = await httpClient
.get(
"/users/{userId}",
{
params: {
userId: "1",
},
},
)
.iWantInformation("user.found");
// Les paramĂštres peuvent ĂȘtre surchargĂ©s
const specificResponse = await httpClient
.get(
"/users/{userId}",
{
params: {
userId: "1",
},
headers: {
"Accept-Language": "en-US", // Surcharge le paramÚtre par défaut
"Authorization": "Bearer token" // Ajout d'un nouveau paramĂštre
}
},
)
.iWantInformation("user.found");
Dans cet exemple :
- La premiĂšre requĂȘte utilise les paramĂštres par dĂ©faut dĂ©finis prĂ©cĂ©demment.
- La deuxiĂšme requĂȘte surcharge le paramĂštre
Accept-Language
et ajoute un nouvel en-tĂȘteAuthorization
.
import { HttpClient } from "@duplojs/http-client";
export const httpClient = new HttpClient({
baseUrl: "your.server.url",
});
httpClient.setDefaultRequestParams({
credentials: "include", // include, same-origin, omit
mode: "cors", // cors, no-cors, same-origin, navigate
headers: {
// set your default headers here
// example:
// "Content-Type": "application/json",
},
params: {
// set your default query params here
// example:
// "api_key": "your_api_key",
},
query: {
// set your default query params here
// example:
// "user_id": "your_user_id",
},
redirect: "follow", // manual, follow, error
referrer: "client", // no-referrer, client
referrerPolicy: "no-referrer-when-downgrade", // no-referrer, no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url
integrity: "",
signal: null, // (type: AbortSignal | null)
window: null,
keepalive: false, // (type: boolean)
dispatcher: undefined, // (type: Dispatcher | undefined)
duplex: "half", // half, undefined
});
Intercepteurs
Les intercepteurs permettent dâintercepter et de modifier les requĂȘtes avant leur envoi ou les rĂ©ponses avant leur traitement. Cette fonctionnalitĂ© est particuliĂšrement utile pour :
- Ajouter des en-tĂȘtes dâauthentification dynamiques
- Effectuer des transformations de données
- Logger les requĂȘtes/rĂ©ponses
- Gérer les erreurs de maniÚre centralisée
Exemple dâutilisation
// Intercepteur de requĂȘte
httpClient.setInterceptor("request", async (request) => {
// Ajout d'un token d'authentification
if (request.headers) {
request.headers["Authorization"] = `Bearer ${await getToken()}`;
}
return request;
});
// Intercepteur de réponse
httpClient.setInterceptor("response", async (response) => {
// Gestion centralisée des erreurs
if (response.code === 401) {
await refreshToken();
// Relancer la requĂȘte...
}
return response;
});
Dans cet exemple :
- Lâintercepteur de requĂȘte ajoute un token dâauthentification Ă chaque requĂȘte
- Lâintercepteur de rĂ©ponse gĂšre le rafraĂźchissement du token en cas dâexpiration
Types dâintercepteurs
Type | Signature | Description |
---|---|---|
request | (request: RequestDefinition) => Promise<RequestDefinition> | Modifie la requĂȘte avant son envoi |
response | (response: Response) => Promise<Response> | Transforme la réponse avant son traitement |
Cas dâutilisation courants
Authentification dynamique
httpClient.setInterceptor("request", async (request) => {
const token = await getToken();
if (request.headers) {
request.headers["Authorization"] = `Bearer ${token}`;
}
return request;
});
Logging des requĂȘtes
httpClient.setInterceptor("request", (request) => {
console.log(`[${new Date().toISOString()}] ${request.method} ${request.path}`);
return request;
});
Transformation du corp des réponses
httpClient.setInterceptor("response", async (response) => {
if (response.body) {
response.body = {
...response.body,
timestamp: new Date().toISOString(),
}
};
return response;
});
Gestion globale des erreurs
httpClient.setInterceptor("response", async (response) => {
if (!response.ok) {
switch (response.code) {
case 401:
await refreshToken();
break;
case 403:
redirectToLogin();
break;
default:
// Ne rien faire pour laisser le traitement de l'erreur Ă la route
break;
}
}
return response;
});
Hooks
Les hooks permettent dâexĂ©cuter du code en fonction de certaines conditions sur les rĂ©ponses HTTP. Contrairement aux intercepteurs qui modifient les requĂȘtes/rĂ©ponses, les hooks sont purement rĂ©actifs et ne peuvent pas modifier les rĂ©ponses.
Types de hooks disponibles
Type | Description |
---|---|
code | Se déclenche sur un code HTTP spécifique |
general | Se déclenche sur une plage de codes HTTP (200-299, 400-499, 500-599) |
error | Se déclenche quand response.ok est false |
information | Se déclenche sur une information spécifique |
Exemples dâutilisation des hooks
Hook sur un code HTTP spécifique
httpClient.hooks.add({
type: "code",
value: 401,
callback: async (response) => {
await refreshToken();
}
});
Hook sur une plage de codes HTTP
httpClient.hooks.add({
type: "general",
value: 200, // Se déclenche pour tous les codes 2XX
callback: async (response) => {
console.log("RequĂȘte rĂ©ussie:", response);
}
});
Hook sur les erreurs
httpClient.hooks.add({
type: "error",
callback: async (response) => {
console.error("Une erreur est survenue:", response);
notifyError(response);
}
});
Hook sur une information spécifique
httpClient.hooks.add({
type: "information",
value: "user.found",
callback: async (response) => {
updateUserCache(response.body);
}
});
Points importants :
- Les hooks sont exécutés aprÚs le traitement de la réponse
- Plusieurs hooks peuvent ĂȘtre dĂ©finis pour le mĂȘme type/valeur
- Les hooks ne peuvent pas modifier la réponse
- Les hooks sont exécutés de maniÚre asynchrone
Cas dâutilisation courants des hooks
- Notification utilisateur
// Afficher une notification pour chaque erreur
httpClient.hooks.add({
type: "error",
callback: (response) => {
showToast({
type: "error",
message: "Une erreur est survenue"
});
}
});
// Afficher un message de succÚs spécifique
httpClient.hooks.add({
type: "information",
value: "user.created",
callback: () => {
showToast({
type: "success",
message: "Utilisateur créé avec succÚs"
});
}
});
- RafraĂźchissement automatique
// Actualiser la liste des utilisateurs
httpClient.hooks.add({
type: "information",
value: "user.updated",
callback: () => {
refreshUserList();
}
});
- Redirection
// Rediriger vers la page de connexion
httpClient.hooks.add({
type: "code",
value: 401,
callback: () => {
router.push("/login");
}
});
Ces exemples illustrent des cas dâutilisation simples mais frĂ©quents dans une application web moderne.
// TODO: proposer un mini projet duplo/vue pour montrer comment utiliser le client http dans un vrai projet