@arc-js/cp-request
v0.0.2
Published
CP-REQUEST est une bibliothèque TypeScript complète pour gérer les requêtes HTTP et WebSocket avec une API unifiée et type-safe. Elle offre une solution robuste pour les communications client-serveur avec gestion automatique des erreurs, validation des sc
Readme
@arc-js/cp-request
@arc-js/cp-request est une bibliothèque TypeScript complète pour gérer les requêtes HTTP et WebSocket avec une API unifiée et type-safe. Elle offre une solution robuste pour les communications client-serveur avec gestion automatique des erreurs, validation des schémas et support multi-langue.
✨ Fonctionnalités Principales
🌐 HTTP Request (HttpRequest)
- Méthodes HTTP complètes : GET, POST, PUT, DELETE
- Gestion intelligente des erreurs : mapping automatique des codes HTTP vers des types de notification
- Validation de schéma : intégration avec JON pour la validation des données
- Support multi-langue : messages d'erreur en français et anglais
- Authentification : support Basic Auth
- Types de réponse : JSON, text, arraybuffer, blob
- Intercepteurs : actions personnalisées pour succès/erreur
🔌 WebSocket Request (WsRequest)
- Connexion WebSocket managée : avec reconnexion automatique
- File d'attente de messages : messages stockés si non connecté
- API unifiée : même interface que HttpRequest pour la cohérence
- Gestion d'état : suivi de la connexion et tentatives de reconnexion
- Validation des messages : schémas JON pour les données entrantes
🛡️ Sécurité & Fiabilité
- Validation stricte : des paramètres d'entrée
- Gestion des timeouts : configuration flexible
- Protection contre les erreurs : fallbacks et valeurs par défaut
- Logs détaillés : en mode développement seulement
📦 Installation
Via npm/yarn/pnpm
npm install @arc-js/cp-request
# ou
yarn add @arc-js/cp-request
# ou
pnpm add @arc-js/cp-requestImportation directe (CDN)
<script src="@arc-js/cp-request/cp-request.all.js"></script>🚀 Démarrage Rapide
TypeScript/ES Modules
import { HttpRequest, WsRequest } from '@arc-js/cp-request';
// ou
import CPRequest from '@arc-js/cp-request';CommonJS
const { HttpRequest, WsRequest } = require('@arc-js/cp-request');Navigateur (global)
<script src="@arc-js/cp-request/cp-request.all.js"></script>
<script>
// Disponible globalement
const httpRequest = new HttpRequest();
const wsRequest = new WsRequest();
</script>📚 Documentation API
Configuration Initiale
// Configuration de base
const httpRequest = new HttpRequest()
.initConfig('http://api.example.com', 'fr');
const wsRequest = new WsRequest()
.initConfig('wss://ws.example.com', 'en');Interface HTTPRequestParams
interface HTTPRequestParams {
method: "GET" | "POST" | "PUT" | "DELETE";
path: string;
headers?: Record<string, string>;
params?: Record<string, any>;
body?: any;
responseType?: "json" | "arraybuffer" | "blob" | "text";
auth?: {
username: string;
password: string;
};
}Interface WSRequestParams
interface WSRequestParams {
path: string;
headers?: Record<string, string>;
auth?: {
username: string;
password: string;
};
autoReconnect?: boolean;
reconnectDelay?: number;
maxReconnectAttempts?: number;
}Réponse Standardisée
interface HTTPRequestResponse {
response: {
code: number;
type: string; // "success", "error", "warning", "info", "error_auth", "error_auth_required"
message: any;
};
data: any;
error?: any;
errors?: any[];
}🔧 Utilisation Détaillée
Requêtes HTTP
Récupérer plusieurs éléments (findAllRequests)
const httpRequest = new HttpRequest()
.initConfig('http://api.example.com', 'fr');
httpRequest.findAllRequests(
{
method: 'GET',
path: '/users',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer token'
},
params: {
page: 1,
limit: 20,
sort: 'name'
}
},
{}, // otherParams
(response) => {
console.log('Succès:', response.data); // Tableau d'utilisateurs
},
(response) => {
console.error('Erreur:', response.error);
}
);Récupérer un seul élément (findOneRequest)
httpRequest.findOneRequest(
{
method: 'GET',
path: '/users/123'
},
{},
(response) => {
console.log('Utilisateur:', response.data);
},
(response) => {
console.error('Erreur:', response.errors);
}
);Créer une ressource (POST)
httpRequest.findOneRequest(
{
method: 'POST',
path: '/users',
body: {
name: 'John Doe',
email: '[email protected]'
}
},
{},
(response) => {
console.log('Créé avec succès:', response.data);
}
);Authentification Basic
httpRequest.findAllRequests(
{
method: 'GET',
path: '/secure-data',
auth: {
username: 'admin',
password: 'secret123'
}
}
);WebSocket
Connexion WebSocket
const wsRequest = new WsRequest()
.initConfig('wss://ws.example.com', 'fr');
wsRequest.connect({
path: '/socket',
autoReconnect: true,
reconnectDelay: 3000,
maxReconnectAttempts: 5
});Envoyer et recevoir des messages
// Récupérer plusieurs éléments via WebSocket
wsRequest.findAllMessages(
{
action: 'GET_USERS',
filters: { active: true }
},
(response) => {
console.log('Utilisateurs actifs:', response.data);
},
(response) => {
console.error('Erreur WebSocket:', response.error);
}
);
// Récupérer un élément spécifique
wsRequest.findOneMessage(
{
action: 'GET_USER',
id: 456
},
(response) => {
console.log('Utilisateur:', response.data);
}
);Gestion de la file d'attente
Les messages sont automatiquement mis en file d'attente si la connexion WebSocket n'est pas encore établie et envoyés une fois la connexion rétablie.
Actions Personnalisées
Custom Map Actions
const customMapAction: HttpRequestMapAction = (
code,
type,
message,
data,
cleanedData,
error,
errors
) => {
// Logique personnalisée de transformation
return {
response: { code, type, message },
data: cleanedData ? cleanedData.processed : null,
metadata: { timestamp: new Date().toISOString() },
error,
errors
};
};
httpRequest.findAllRequests(
{ method: 'GET', path: '/data' },
{},
new JON.Object('fr'), // schéma
customMapAction, // mapAction personnalisée
undefined, // errMapAction (par défaut)
undefined, // successAction (par défaut)
undefined // errorAction (par défaut)
);Custom Success/Error Actions
const successAction: HttpRequestReponseAction = async (response) => {
// Enregistrer dans l'historique
await logToDatabase({
type: 'request_success',
code: response.response.code,
data: response.data
});
// Afficher une notification
showNotification('Succès', response.response.message);
};
const errorAction: HttpRequestReponseAction = (response) => {
// Journaliser l'erreur
console.error('Request failed:', response);
// Afficher un message d'erreur
alert(`Erreur \${response.response.code}: \${response.response.message}`);
};🎯 Exemples Complets
Exemple 1 : Application avec Authentification
class ApiService {
private http: HttpRequest;
constructor(baseUrl: string, lang: 'fr' | 'en' = 'fr') {
this.http = new HttpRequest().initConfig(baseUrl, lang);
}
async login(email: string, password: string) {
return this.http.findOneRequest(
{
method: 'POST',
path: '/auth/login',
body: { email, password }
},
{},
(response) => {
// Stocker le token
localStorage.setItem('token', response.data.token);
// Rediriger
window.location.href = '/dashboard';
},
(response) => {
// Afficher l'erreur
alert(`Échec de connexion: \${response.response.message}`);
}
);
}
async getProfile() {
const token = localStorage.getItem('token');
return this.http.findOneRequest(
{
method: 'GET',
path: '/profile',
headers: {
'Authorization': `Bearer \${token}`
}
}
);
}
}Exemple 2 : Chat en Temps Réel
class ChatService {
private ws: WsRequest;
private messageHandlers: ((message: any) => void)[] = [];
constructor(wsUrl: string) {
this.ws = new WsRequest().initConfig(wsUrl, 'fr');
this.ws.connect({
path: '/chat',
autoReconnect: true,
reconnectDelay: 2000
});
}
async sendMessage(roomId: string, content: string) {
return this.ws.findOneMessage(
{
action: 'SEND_MESSAGE',
roomId,
content,
timestamp: Date.now()
},
(response) => {
console.log('Message envoyé:', response.data);
},
(response) => {
console.error('Erreur d\'envoi:', response.error);
}
);
}
async getMessages(roomId: string, limit = 50) {
return this.ws.findAllMessages(
{
action: 'GET_MESSAGES',
roomId,
limit
},
(response) => {
// Traiter les messages
response.data.forEach(message => {
this.messageHandlers.forEach(handler => handler(message));
});
}
);
}
onMessage(handler: (message: any) => void) {
this.messageHandlers.push(handler);
}
}Exemple 3 : Upload de Fichiers
async uploadFile(file: File, onProgress?: (progress: number) => void) {
const formData = new FormData();
formData.append('file', file);
formData.append('metadata', JSON.stringify({
name: file.name,
type: file.type,
size: file.size
}));
return this.http.findOneRequest(
{
method: 'POST',
path: '/upload',
body: formData,
// Pas de Content-Type pour FormData, le navigateur le définit automatiquement
},
{
// Configuration fetch supplémentaire
onUploadProgress: (progressEvent) => {
if (onProgress) {
const percent = (progressEvent.loaded / progressEvent.total) * 100;
onProgress(percent);
}
}
},
(response) => {
console.log('Fichier uploadé:', response.data);
showNotification('Succès', 'Fichier uploadé avec succès');
},
(response) => {
console.error('Échec upload:', response.error);
showNotification('Erreur', 'Échec de l\'upload du fichier');
}
);
}🔧 Configuration Avancée
Types de Notification Personnalisés
import { getNotifRequestType } from '@arc-js/cp-request';
// Étendre les types de notification
function customGetNotifRequestType(status: number): string {
const baseType = getNotifRequestType(status);
// Ajouter des types personnalisés
if (status === 429) {
return 'rate_limit';
}
if (status === 503) {
return 'maintenance';
}
return baseType;
}Schémas de Validation Personnalisés
import JON from '@arc-js/jon';
// Créer un schéma de validation personnalisé
const userSchema = new JON.Object('fr').struct({
id: new JON.Number('fr').required(),
name: new JON.String('fr').min(2).max(100).required(),
email: new JON.String('fr').email().required(),
age: new JON.Number('fr').min(0).max(150),
roles: new JON.Array('fr').types(
new JON.Enum('fr').choices('admin', 'user', 'guest')
).default(['user'])
});
// Utiliser le schéma dans une requête
httpRequest.findOneRequest(
{ method: 'GET', path: '/user/123' },
{},
userSchema, // Schéma personnalisé
// ... autres paramètres
);Middleware et Intercepteurs
class RequestLogger {
static async logRequest(urlParams: HTTPRequestParams) {
console.log('[REQUEST]', {
timestamp: new Date().toISOString(),
method: urlParams.method,
path: urlParams.path,
hasBody: !!urlParams.body
});
}
static async logResponse(response: HTTPRequestResponse) {
console.log('[RESPONSE]', {
timestamp: new Date().toISOString(),
code: response.response.code,
type: response.response.type,
hasData: !!response.data
});
}
}
// Envelopper les méthodes existantes
const originalFindAllRequests = HttpRequest.prototype.findAllRequests;
HttpRequest.prototype.findAllRequests = async function(...args) {
await RequestLogger.logRequest(args[0]);
const result = await originalFindAllRequests.apply(this, args);
await RequestLogger.logResponse(result);
return result;
};📋 Table des Codes de Notification
| Code HTTP | Type | Description | Action Recommandée | |-----------|------|-------------|-------------------| | 200 | success | Requête réussie | Traiter les données | | 201 | success | Ressource créée | Mettre à jour l'UI | | 204 | success | Pas de contenu | Rafraîchir si nécessaire | | 400 | error | Mauvaise requête | Corriger les données envoyées | | 401 | error_auth_required | Non authentifié | Rediriger vers login | | 403 | error_auth | Non autorisé | Vérifier les permissions | | 404 | error | Non trouvé | Afficher message d'erreur | | 409 | error_auth | Conflit | Résoudre le conflit de données | | 419 | error_auth_required | Session expirée | Renouveler la session | | 422 | warning | Données invalides | Corriger la validation | | 429 | warning | Trop de requêtes | Attendre avant de réessayer | | 500 | error | Erreur serveur | Signaler l'administrateur | | 503 | error | Service indisponible | Réessayer plus tard |
🚨 Gestion des Erreurs
Erreurs de Réseau
try {
await httpRequest.findAllRequests({
method: 'GET',
path: '/data'
});
} catch (error) {
if (error.message.includes('Failed to fetch')) {
// Pas de connexion internet
showOfflineMessage();
} else if (error.response?.code === 500) {
// Erreur serveur
showServerError();
} else {
// Erreur inconnue
console.error('Erreur inattendue:', error);
}
}Retry Automatique
async function fetchWithRetry(urlParams, maxRetries = 3) {
let lastError;
for (let i = 0; i < maxRetries; i++) {
try {
return await httpRequest.findOneRequest(urlParams);
} catch (error) {
lastError = error;
// Attendre avant de réessayer (backoff exponentiel)
await new Promise(resolve =>
setTimeout(resolve, 1000 * Math.pow(2, i))
);
}
}
throw lastError;
}🔧 Build et Développement
Structure du Projet
@arc-js/cp-request/
├── cp-request.all.js
├── cp-request.all.min.js
├── index.d.ts
├── index.js
├── index.min.d.ts
├── index.min.js
├── package.json
├── tsconfig.json
└── README.md📋 Compatibilité
Navigateurs Supportés
- Chrome 60+
- Firefox 55+
- Safari 12+
- Edge 79+
- Opera 47+
Environnements
- Node.js 18+
- Deno 1.30+
- Bun 1.0+
- React Native (avec polyfill fetch)
- Electron
Dépendances
- @arc-js/jon (pour la validation)
- @arc-js/pajo (pour la manipulation de chemins)
- @arc-js/qust (pour la manipulation de query strings)
- @arc-js/timez (pour la gestion du temps)
🛡️ Meilleures Pratiques
Sécurité
- Toujours valider les entrées : utiliser les schémas JON
- Ne pas exposer les tokens : dans les logs de développement
- Utiliser HTTPS/SSL : toujours en production
- Sanitizer les headers : éviter l'injection de headers
Performance
- Réutiliser les instances : créer une instance par base URL
- Limiter les requêtes parallèles : utiliser une file d'attente si nécessaire
- Mettre en cache : les réponses statiques
- Compresser les données : gzip/brotli pour les bodies volumineux
Maintenance
- Centraliser la configuration : dans un service dédié
- Logger proprement : différents niveaux selon l'environnement
- Versionner les APIs : dans le path (/api/v1/)
- Documenter les endpoints : avec des exemples
📄 Licence
MIT License - Voir le fichier LICENSE pour plus de détails.
🐛 Signaler un Bug
Envoyez nous un mail à l'adresse [email protected] pour :
- Signaler un bug
- Proposer une amélioration
- Poser une question
@arc-js/cp-request - Une solution complète pour les communications client-serveur.
Développé par l'équipe INICODE
