Ce module fournit un client JavaScript prêt à l'emploi pour interagir avec l'API Cocolight. Il encapsule la gestion des appels HTTP, l'authentification, la validation des schémas, la gestion des erreurs, ainsi qu'un ensemble de normalisations des données pour simplifier l'intégration côté client.
npm install @communecter/cocolight-api-client
Ce client facilite l'intégration avec l'API Cocolight en offrant :
- Des appels simplifiés avec
axios
- Un système de validation des entrées/sorties avec AJV
- La gestion automatique du token d'accès et du token de rafraîchissement
- Un circuit breaker configurable
- Un logger intégré avec Pino
- Des outils de transformation de données compatibles MongoDB/EJSON
- La gestion des appels API à partir de métadonnées JSON (endpoints)
Le projet est structuré autour des fichiers suivants :
src/
ApiClient.js # Client principal : logique d'appel, authentification, validation, transformation
EJSONType.js # Support pour les ObjectID MongoDB avec EJSON
endpoints.module.js # Fichier généré avec les définitions d'endpoints (voir ci-dessous)
error.js # Erreurs personnalisées (ApiClientError, CircuitBreakerError)
package.json # Dépendances, scripts, métadonnées
import Cocolight from "@communecter/cocolight-api-client";
const { Api, ApiClient, tokenStorageStrategy } = Cocolight;
const { ApiClient, Api, tokenStorageStrategy } = require("@communecter/cocolight-api-client").default;
Vous pouvez inclure le client directement dans une page HTML :
<script src="https://cdn.jsdelivr.net/npm/@communecter/cocolight-api-client@latest/dist/cocolight-api-client.browser.js"></script>
<script>
const client = new CocolightApiClient.ApiClient({
baseURL: "https://www.communecter.org"
});
client.callEndpoint("AUTHENTICATE_URL", {
email: "EMAIL",
password: "PASSWORD"
}).then(res => {
const { accessToken, refreshToken, user } = res.data;
console.log("✅ Authentifié :", user.username);
})
.then(() => {
return client.callEndpoint("ME_INFO_URL");
})
.then(res => {
console.log("👤 Profil utilisateur :", res.data);
})
.then(() => {
console.log("userId :", client.userId);
})
.catch(err => console.error("Erreur :", err));
</script>
import { ApiClient } from "@communecter/cocolight-api-client";
const client = new ApiClient({
baseURL: "https://mon-api.com"
});
try {
const authResponse = await client.callEndpoint("AUTHENTICATE_URL", {
email: "EMAIL",
password: "PASSWORD"
});
const { accessToken, refreshToken, user } = authResponse.data;
console.log("✅ Authentifié :", user.username);
const meResponse = await client.callEndpoint("ME_INFO_URL");
console.log("👤 Profil utilisateur :", meResponse.data);
console.log("userId :", client.userId);
} catch (error) {
console.error("❌ Erreur durant la session :", error.message, error.details);
}
Option | Type | Description |
---|---|---|
baseURL (requis) |
string |
URL de base de l'API |
accessToken |
string |
Token d'accès initial |
refreshToken |
string |
Token de rafraîchissement |
refreshUrl |
string |
URL pour rafraîchir le token (défaut : /api/cocolight/refreshtoken ) |
endpoints |
Array |
Liste des endpoints (par défaut : endpoints.module.js ) |
timeout |
number |
Timeout en ms (défaut : 30000 ) |
debug |
boolean |
Active le logger debug (défaut : false ) |
maxRetries |
number |
Nombre de tentatives max (défaut : 0 ) |
circuitBreakerThreshold |
number |
Seuil d'erreurs avant blocage (défaut : 5 ) |
circuitBreakerResetTime |
number |
Délai de réactivation (en ms, défaut : 60000 ) |
fromJSONValue |
boolean |
Applique EJSON.fromJSONValue sur les réponses (défaut : true ) |
Effectue un appel à un endpoint défini via endpoints.module.js
.
-
constant
: nom unique du endpoint -
data
: objet avecpathParams
et données du corps -
transform
:true
(par défaut),false
, ou fonction de transformation -
validate
:true
(par défaut), permet de valider la réponse via AJV
Gère le token d'accès.
Gère le token de rafraîchissement.
Réinitialise la session utilisateur.
-
refreshSuccess
— déclenché après un rafraîchissement réussi -
validationError
— déclenché si AJV détecte des erreurs -
sessionReset
— déclenché parresetSession()
Depuis package.json
:
-
npm run test
— Lance les tests via Jest -
npm run exemple
— Lance un exemple -
npm run lint
/lint:fix
— Vérifie et corrige le code avec ESLint -
npm run build
— Génère les builds navigateur + Node via Webpack -
npm run generate:*
— Génère les modules, documentation ou données de test depuis les fichiers JSON d’API
-
axios
&axios-retry
— pour les appels HTTP robustes -
ajv
&ajv-formats
— pour la validation JSON Schema -
ejson
— pour la compatibilité MongoDB (ObjectID, dates...) -
pino
— logger performant, compatible navigateur -
events
— pour la gestion des événements custom côté client
Chaque endpoint dans endpoints.module.js
suit le schéma :
{
constant: "GET_USER_INFO",
method: "GET",
path: "/api/users/{userId}",
auth: "bearer",
pathParams: { type: "object", properties: { userId: { type: "string" } }, required: ["userId"] },
request: { ...schemaAjv },
responses: { "200": { ...schemaAjv } },
postActions: [
{ type: "setToken", path: "accessToken" },
{ type: "setUserId", path: "user._id" }
]
}
Un circuit breaker est intégré :
- Blocage temporaire après trop d’échecs (
circuitBreakerThreshold
) - Réactivation après un délai (
circuitBreakerResetTime
) - Journalisation complète avec
pino
Chaque appel est validé avant (requête) et après (réponse) via AJV. En cas d’erreur, l’événement validationError
est déclenché avec les détails.
Si une réponse retourne 401, le client tente automatiquement un appel POST vers refreshUrl
avec le refreshToken
, et réessaie l’appel initial si réussi.
Les réponses sont normalisées :
- Champs dates, images, IDs MongoDB
- Structures communes comme
results
,news
,notif
,replies
, etc. - Conversion EJSON via
EJSON.fromJSONValue()
Dans les données pathParams
, il est possible d’utiliser des alias dynamiques :
pathParams: {
userId: "@userId",
accessToken: "@accessToken"
}
La classe Api
fournit une interface unifiée pour gérer les utilisateurs, organisations, projets et actualités. Elle encapsule les appels à ApiClient
et permet de manipuler les entités comme des objets métier avec des méthodes pratiques.
Crée une instance d’Api
authentifiée.
const api = await Api.userLogin("john@doe.com", "password123", { baseURL: "https://api.monapp.com" });
const user = await api.me();
Retourne une instance UserApi
si tu veux gérer manuellement l’authentification :
const userApi = Api.userApi({ baseURL: "..." });
const user = await userApi.login("email", "mdp");
Retourne l'utilisateur actuellement connecté (User
).
const user = await api.me();
console.log(user.data.email);
Crée une instance User
(autre que le connecté).
const otherUser = await api.user({ slug: "caroline" });
console.log(otherUser.data.description);
Retourne une instance d’Organization
. Récupère automatiquement le profil public.
const orga = await api.organization({ slug: "asso-verte" });
console.log(orga.data.name);
Retourne une instance Project
. Récupère automatiquement le profil public.
const projet = await api.project({ id: "647..." });
Les entités suivantes partagent une API commune grâce aux mixins : User
, Organization
, Project
, News
.
Propriété | Description |
---|---|
data |
Proxy combiné serverData + draftData
|
draftData |
Données modifiables avant save()
|
initialDraftData |
Snapshot initial pour détection des changements |
serverData |
Dernières données serveur |
isConnected |
Vrai si ApiClient a un token |
userId |
ID de l'utilisateur connecté |
isMe |
Vrai si l'entité correspond à l'utilisateur courant |
Sauvegarde les données via les blocs modifiés (UPDATE_BLOCK_*
, ADD_*
).
Recharge les données depuis le serveur.
Retourne true
si le draft est différent du snapshot initial.
Méthode | Description |
---|---|
updateDescription |
Met à jour les champs shortDescription , description , descMentions
|
updateInfo |
Nom, email, téléphone, etc. |
updateSocial |
Réseaux sociaux (Facebook, GitHub...) |
updateLocality |
Adresse, géolocalisation |
updateSlug |
Slug de l’URL |
updateImageProfil |
Upload de l’avatar |
Créées à partir d’un User
, Organization
ou Project
.
const news = await orga.news();
news.data.text = "Nouvelle actu !";
await news.save();
Méthode | Description |
---|---|
addMention({ slug | id }) |
Ajoute une mention à la news |
addImage(file) |
Ajoute une image (via validation MIME) |
addFile(file) |
Ajoute un fichier (PDF, CSV...) |
delete() |
Supprime la news |
refresh() |
Recharge la news |
get() |
Récupère les données à partir de l’ID |
Toutes les entités sont basées sur les constantes de schéma :
ADD_*
UPDATE_BLOCK_*
PROFIL_IMAGE
Ces constantes sont mappées automatiquement pour les appels save()
, updateX()
, etc.
const projet = await api.project({ slug: "bio-bazar" });
projet.data.description = "Nouveau descriptif de mon projet.";
if (projet.hasChanges()) {
await projet.save();
}
Une instance User
offre un ensemble de méthodes métier pour interagir avec les projets, organisations et actualités de l’utilisateur.
const user = await api.user({ slug: "caroline" });
await user.get();
Un utilisateur peut créer :
const orga = await user.organization({ name: "Ma nouvelle asso" });
await orga.save(); // appelle ADD_ORGANIZATION puis rafraîchit les données
const projet = await user.project({ name: "Mon projet citoyen" });
await projet.save(); // appelle ADD_PROJECT
const news = await user.news({ text: "Hello world 🌍" });
await news.save(); // appelle ADD_NEWS
Retourne la liste des projets créés ou co-administrés par l’utilisateur.
const { results: projets } = await user.getProjects();
console.log("Projets liés à l'utilisateur :", projets.map(p => p.data.name));
Retourne les organisations où il est membre (admin ou non).
const { results: orgs } = await user.getOrganizations();
console.log("Organisations :", orgs.map(o => o.data.name));
Retourne toutes les actualités liées à l’utilisateur.
const newsList = await user.getNews({ indexStep: 5 });
Crée une nouvelle actualité, ou charge une existante si id
est fourni.
const actu = await user.news({ text: "Ceci est une nouvelle actu" });
await actu.save();
Les méthodes updateX
disponibles sont :
updateDescription(data)
updateInfo(data)
updateSocial(data)
updateLocality(data)
updateSlug({ slug })
updateImageProfil({ profil_avatar })
updateSettings({ type, value })
await user.changePassword({
oldPassword: "secret1",
newPassword: "secret2",
newPassword2: "secret2"
});
await user.delete({ reason: "Je souhaite quitter la plateforme" });
MIT
Thomas Craipeau