Documentation Index
Fetch the complete documentation index at: https://docs.lovi.ai/llms.txt
Use this file to discover all available pages before exploring further.
Introduction
Ce guide couvre les bonnes pratiques de sécurité, l’optimisation des performances et les directives d’intégration pour vous aider à construire des applications robustes avec l’API Lovi.
🔒 Bonnes Pratiques de Sécurité
Gestion des Clés API
✅ À FAIRE :
- Stockez les clés API dans des variables d’environnement ou un système de gestion de configuration sécurisé
- Utilisez des clés API différentes pour chaque environnement (développement, staging, production)
- Renouvelez les clés API régulièrement (recommandé : tous les 90 jours)
- Surveillez l’utilisation des clés API et configurez des alertes pour les activités inhabituelles
- Appliquez le principe du moindre privilège (clés séparées pour différentes fonctions si disponible)
❌ À NE PAS FAIRE :
- Coder en dur les clés API dans le code source ou les enregistrer dans le contrôle de version
- Partager les clés API par e-mail, chat ou autres canaux non sécurisés
- Utiliser les clés API de production dans les environnements de développement
- Enregistrer les clés API complètes dans les logs applicatifs
Sécurité de l’Authentification
// ✅ Good - Store in environment variables
const ACCESS_KEY = process.env.LOVI_ACCESS_KEY;
// ❌ Bad - Hardcoded in source
const ACCESS_KEY = 'your-actual-api-key-here';
Sécurité des Requêtes
Utilisez toujours HTTPS :
// ✅ Good - HTTPS only
const BASE_URL = 'https://cloud.lovi.ai';
// ❌ Bad - Insecure HTTP
const BASE_URL = 'http://cloud.lovi.ai';
Validez les données d’entrée :
function validatePhoneNumber(number) {
// Remove any non-digit characters
const cleaned = number.replace(/\D/g, '');
// Check if it's a valid international format
if (!/^\d{10,15}$/.test(cleaned)) {
throw new Error('Invalid phone number format');
}
return cleaned;
}
Gestion des Connexions
Réutilisez les connexions HTTP :
// ✅ Good - Reuse connection pool
const https = require('https');
const agent = new https.Agent({
keepAlive: true,
maxSockets: 10
});
const fetch = require('node-fetch');
async function makeRequest(url, data) {
return fetch(url, {
method: 'POST',
agent: agent,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
}
Traitement par Lots
Traitez les notifications par lots :
async function sendNotificationsBatch(notifications, batchSize = 10) {
const results = [];
for (let i = 0; i < notifications.length; i += batchSize) {
const batch = notifications.slice(i, i + batchSize);
const batchPromises = batch.map(notification =>
sendNotification(notification)
);
const batchResults = await Promise.allSettled(batchPromises);
results.push(...batchResults);
// Rate limiting: wait between batches
if (i + batchSize < notifications.length) {
await sleep(100); // 100ms delay
}
}
return results;
}
Stratégies de Mise en Cache
Mettez en cache les informations des templates :
class TemplateCache {
constructor(ttl = 3600000) { // 1 hour TTL
this.cache = new Map();
this.ttl = ttl;
}
async getTemplate(templateName) {
const cached = this.cache.get(templateName);
if (cached && Date.now() - cached.timestamp < this.ttl) {
return cached.data;
}
const template = await fetchTemplateFromAPI(templateName);
this.cache.set(templateName, {
data: template,
timestamp: Date.now()
});
return template;
}
}
📊 Limitation et Contrôle du Débit
Comprendre les Limites de Débit
| Opération | Limite | Fenêtre |
|---|
| Authentification | 10 requêtes | 1 minute |
| Notifications | 100 requêtes | 1 minute |
| Récupération de templates | 50 requêtes | 1 minute |
| Création de templates | 5 requêtes | 1 heure |
Gestion des Limites de Débit
class RateLimitHandler {
constructor() {
this.requestQueue = [];
this.processing = false;
}
async makeRequest(url, data) {
return new Promise((resolve, reject) => {
this.requestQueue.push({ url, data, resolve, reject });
this.processQueue();
});
}
async processQueue() {
if (this.processing || this.requestQueue.length === 0) return;
this.processing = true;
while (this.requestQueue.length > 0) {
const request = this.requestQueue.shift();
try {
const response = await this.executeRequest(request);
request.resolve(response);
} catch (error) {
if (error.status === 429) {
// Rate limited - put request back and wait
this.requestQueue.unshift(request);
await sleep(error.retryAfter * 1000);
continue;
}
request.reject(error);
}
// Small delay between requests
await sleep(100);
}
this.processing = false;
}
}
🏗️ Modèles d’Architecture
Modèle Repository
class NotificationRepository {
constructor(apiClient) {
this.apiClient = apiClient;
}
async sendWhatsAppNotification(notification) {
const payload = this.formatWhatsAppPayload(notification);
return await this.apiClient.post('/notify', payload);
}
async sendVoiceNotification(notification) {
const payload = this.formatVoicePayload(notification);
return await this.apiClient.post('/notify/voice', payload);
}
formatWhatsAppPayload(notification) {
return {
contact: {
number: this.validatePhoneNumber(notification.phoneNumber),
name: notification.contactName
},
language_template: notification.language,
name_template: notification.templateName,
recipient_id: notification.phoneNumber,
notification_type: notification.type,
campaign_name: notification.campaign
};
}
}
Modèle Factory pour les Notifications
class NotificationFactory {
static create(type, data) {
switch (type) {
case 'whatsapp':
return new WhatsAppNotification(data);
case 'voice':
return new VoiceNotification(data);
default:
throw new Error(`Unknown notification type: ${type}`);
}
}
}
class WhatsAppNotification {
constructor(data) {
this.contact = { number: data.phoneNumber, name: data.name };
this.template = data.templateName;
this.type = 'whatsapp';
}
toPayload() {
return {
contact: this.contact,
name_template: this.template,
// ... other properties
};
}
}
🧪 Stratégies de Tests
Tests Unitaires
// Mock API responses for testing
const mockAPIClient = {
post: jest.fn()
};
describe('NotificationService', () => {
test('should send WhatsApp notification successfully', async () => {
mockAPIClient.post.mockResolvedValue({
success: true,
notification_id: 'test-123'
});
const service = new NotificationService(mockAPIClient);
const result = await service.sendWhatsApp({
phoneNumber: '34666033135',
templateName: 'welcome',
contactName: 'John Doe'
});
expect(result.success).toBe(true);
expect(mockAPIClient.post).toHaveBeenCalledWith('/notify', expect.any(Object));
});
});
Tests d’Intégration
// Use test environment with real API calls
describe('Integration Tests', () => {
const testApiKey = process.env.TEST_API_KEY;
const apiClient = new LoviAPIClient(testApiKey);
test('should handle authentication flow', async () => {
const authResult = await apiClient.authenticate(
process.env.TEST_EMAIL,
process.env.TEST_PASSWORD
);
expect(authResult.access_token).toBeDefined();
expect(authResult.token_type).toBe('bearer');
});
});
🔍 Surveillance et Journalisation
Journalisation Structurée
const logger = require('winston');
logger.configure({
format: logger.format.combine(
logger.format.timestamp(),
logger.format.errors({ stack: true }),
logger.format.json()
),
transports: [
new logger.transports.File({ filename: 'api-calls.log' })
]
});
async function loggedAPICall(endpoint, data) {
const requestId = generateRequestId();
logger.info('API request started', {
request_id: requestId,
endpoint: endpoint,
method: 'POST',
timestamp: new Date().toISOString()
});
try {
const response = await makeAPICall(endpoint, data);
logger.info('API request completed', {
request_id: requestId,
endpoint: endpoint,
status_code: response.status,
success: true,
duration: Date.now() - startTime
});
return response;
} catch (error) {
logger.error('API request failed', {
request_id: requestId,
endpoint: endpoint,
error: error.message,
status_code: error.statusCode,
duration: Date.now() - startTime
});
throw error;
}
}
Vérifications de Santé
class APIHealthMonitor {
constructor(apiClient) {
this.apiClient = apiClient;
this.healthStatus = { healthy: true, lastCheck: null };
}
async checkHealth() {
try {
const start = Date.now();
await this.apiClient.get('/templates?limit=1');
const duration = Date.now() - start;
this.healthStatus = {
healthy: true,
lastCheck: new Date().toISOString(),
responseTime: duration,
status: 'operational'
};
} catch (error) {
this.healthStatus = {
healthy: false,
lastCheck: new Date().toISOString(),
error: error.message,
status: 'degraded'
};
}
return this.healthStatus;
}
}
📱 Intégration Mobile et Frontend
Exemple de Hook React
import { useState, useEffect } from 'react';
function useLoviNotification() {
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const sendNotification = async (notificationData) => {
setLoading(true);
setError(null);
try {
const response = await fetch('/api/send-notification', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(notificationData)
});
if (!response.ok) {
throw new Error('Failed to send notification');
}
const result = await response.json();
return result;
} catch (err) {
setError(err.message);
throw err;
} finally {
setLoading(false);
}
};
return { sendNotification, loading, error };
}
🔧 Gestion de la Configuration
Configuration par Environnement
// config/lovi.js
module.exports = {
development: {
baseURL: 'https://cloud.lovi.ai',
accessKey: process.env.LOVI_DEV_ACCESS_KEY,
rateLimits: {
requests: 10,
window: 60000 // 1 minute
},
retries: 3,
timeout: 30000
},
production: {
baseURL: 'https://cloud.lovi.ai',
accessKey: process.env.LOVI_PROD_ACCESS_KEY,
rateLimits: {
requests: 100,
window: 60000
},
retries: 5,
timeout: 60000
}
};
Configuration du Client
class LoviAPIClient {
constructor(config) {
this.baseURL = config.baseURL;
this.accessKey = config.accessKey;
this.timeout = config.timeout;
this.rateLimiter = new RateLimiter(config.rateLimits);
this.retryCount = config.retries;
}
async request(endpoint, options = {}) {
await this.rateLimiter.acquire();
const url = `${this.baseURL}${endpoint}?access_key=${this.accessKey}`;
const config = {
timeout: this.timeout,
...options
};
return this.retryRequest(url, config);
}
}
📚 Documentation et Directives d’Équipe
Documentation du Code
/**
* Send a WhatsApp notification using a template
*
* @param {Object} notification - Notification data
* @param {string} notification.phoneNumber - Recipient phone number (international format, no +)
* @param {string} notification.templateName - Name of approved template
* @param {string} notification.language - Language code (e.g., 'es_ES', 'en_US')
* @param {Object} notification.variables - Template variables
* @param {string} [notification.scheduledTime] - ISO 8601 datetime for scheduling
* @param {string} [notification.timezone] - IANA timezone code
*
* @returns {Promise<Object>} API response with notification_id
*
* @throws {ValidationError} When required parameters are missing
* @throws {APIError} When API request fails
*
* @example
* const result = await sendWhatsAppNotification({
* phoneNumber: '34666033135',
* templateName: 'welcome_user',
* language: 'es_ES',
* variables: { name: 'Juan', course: 'JavaScript' }
* });
*/
async function sendWhatsAppNotification(notification) {
// Implementation...
}
Standards d’Équipe
Liste de Contrôle pour l’Intégration API :
- ✅ Implémenter une gestion des erreurs appropriée
- ✅ Ajouter la journalisation des requêtes/réponses
- ✅ Inclure des tests unitaires
- ✅ Valider les paramètres d’entrée
- ✅ Gérer les limites de débit
- ✅ Utiliser des configurations spécifiques à l’environnement
- ✅ Documenter toutes les fonctions et classes
- ✅ Implémenter des vérifications de santé
- ✅ Ajouter la surveillance et les alertes
🚨 Pièges Courants et Solutions
Piège : Valeurs Codées en Dur
// ❌ Bad
const templateName = 'welcome_template_final_v2';
// ✅ Good
const templateName = config.templates.welcome;
Piège : Mauvaise Gestion des Erreurs
// ❌ Bad
try {
await sendNotification(data);
} catch (error) {
console.log('Error:', error);
}
// ✅ Good
try {
await sendNotification(data);
} catch (error) {
logger.error('Notification failed', {
error: error.message,
data: sanitizeForLog(data),
stack: error.stack
});
throw new NotificationError(
'Failed to send notification',
error.statusCode,
error.requestId
);
}
Piège : Ignorer les Limites de Débit
// ❌ Bad - Send all at once
notifications.forEach(notification => {
sendNotification(notification);
});
// ✅ Good - Respect rate limits
await sendNotificationsBatch(notifications, 10);
N’oubliez pas : suivre ces bonnes pratiques vous aidera à construire des intégrations plus fiables, sécurisées et maintenables avec l’API Lovi.