Zum Hauptinhalt springen

Einführung

Dieser Leitfaden behandelt Sicherheits-Best Practices, Leistungsoptimierung und Integrationsrichtlinien, um Ihnen bei der Erstellung robuster Anwendungen mit der Lovi API zu helfen.

🔒 Sicherheits-Best Practices

API-Schlüssel-Verwaltung

✅ TUN:
  • API-Schlüssel in Umgebungsvariablen oder sicherer Konfigurationsverwaltung speichern
  • Verschiedene API-Schlüssel für verschiedene Umgebungen verwenden (Entwicklung, Staging, Produktion)
  • API-Schlüssel regelmäßig rotieren (empfohlen: alle 90 Tage)
  • API-Schlüssel-Nutzung überwachen und Alarme für ungewöhnliche Aktivitäten einrichten
  • Das Prinzip der geringsten Berechtigung anwenden (separate Schlüssel für verschiedene Funktionen, falls verfügbar)
❌ NICHT TUN:
  • API-Schlüssel in Quellcode hartcodieren oder in Versionskontrolle festschreiben
  • API-Schlüssel per E-Mail, Chat oder anderen unsicheren Kanälen teilen
  • Produktions-API-Schlüssel in Entwicklungs-Umgebungen verwenden
  • Vollständige API-Schlüssel in Anwendungslogs protokollieren

Authentifizierungs-Sicherheit

// ✅ Gut - In Umgebungsvariablen speichern
const ACCESS_KEY = process.env.LOVI_ACCESS_KEY;

// ❌ Schlecht - Im Quellcode hartcodiert
const ACCESS_KEY = 'your-actual-api-key-here';

Anfrage-Sicherheit

Immer HTTPS verwenden:
// ✅ Gut - Nur HTTPS
const BASE_URL = 'https://cloud.lovi.ai';

// ❌ Schlecht - Unsicheres HTTP
const BASE_URL = 'http://cloud.lovi.ai';
Eingabedaten validieren:
function validatePhoneNumber(number) {
  // Alle Nicht-Ziffern-Zeichen entfernen
  const cleaned = number.replace(/\D/g, '');

  // Überprüfen, ob es ein gültiges internationales Format ist
  if (!/^\d{10,15}$/.test(cleaned)) {
    throw new Error('Ungültiges Telefonnummernformat');
  }

  return cleaned;
}

⚡ Leistungsoptimierung

Verbindungsverwaltung

HTTP-Verbindungen wiederverwenden:
// ✅ Gut - Verbindungspool wiederverwenden
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)
  });
}

Batch-Verarbeitung

Benachrichtigungen in Batches verarbeiten:
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);

    // Ratenbegrenzung: zwischen Batches warten
    if (i + batchSize < notifications.length) {
      await sleep(100); // 100ms Verzögerung
    }
  }

  return results;
}

Caching-Strategien

Template-Informationen cachen:
class TemplateCache {
  constructor(ttl = 3600000) { // 1 Stunde 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;
  }
}

📊 Ratenbegrenzung & Drosselung

Ratenlimits verstehen

OperationLimitFenster
Authentifizierung10 Anfragen1 Minute
Benachrichtigungen100 Anfragen1 Minute
Template-Abruf50 Anfragen1 Minute
Template-Erstellung5 Anfragen1 Stunde

Ratenlimit-Behandlung

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) {
          // Ratenbegrenzt - Anfrage zurückstellen und warten
          this.requestQueue.unshift(request);
          await sleep(error.retryAfter * 1000);
          continue;
        }
        request.reject(error);
      }

      // Kleine Verzögerung zwischen Anfragen
      await sleep(100);
    }

    this.processing = false;
  }
}

🏗️ Architekturmuster

Repository-Muster

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
    };
  }
}

Factory-Muster für Benachrichtigungen

class NotificationFactory {
  static create(type, data) {
    switch (type) {
      case 'whatsapp':
        return new WhatsAppNotification(data);
      case 'voice':
        return new VoiceNotification(data);
      default:
        throw new Error(`Unbekannter Benachrichtigungstyp: ${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,
      // ... andere Eigenschaften
    };
  }
}

🧪 Teststrategien

Unit-Tests

// API-Antworten für Tests mocken
const mockAPIClient = {
  post: jest.fn()
};

describe('NotificationService', () => {
  test('should WhatsApp-Benachrichtigung erfolgreich senden', 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));
  });
});

Integrationstests

// Testumgebung mit realen API-Aufrufen verwenden
describe('Integrationstests', () => {
  const testApiKey = process.env.TEST_API_KEY;
  const apiClient = new LoviAPIClient(testApiKey);

  test('should Authentifizierungsablauf behandeln', 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');
  });
});

🔍 Monitoring & Logging

Strukturiertes Logging

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-Anfrage gestartet', {
    request_id: requestId,
    endpoint: endpoint,
    method: 'POST',
    timestamp: new Date().toISOString()
  });

  try {
    const response = await makeAPICall(endpoint, data);

    logger.info('API-Anfrage abgeschlossen', {
      request_id: requestId,
      endpoint: endpoint,
      status_code: response.status,
      success: true,
      duration: Date.now() - startTime
    });

    return response;
  } catch (error) {
    logger.error('API-Anfrage fehlgeschlagen', {
      request_id: requestId,
      endpoint: endpoint,
      error: error.message,
      status_code: error.statusCode,
      duration: Date.now() - startTime
    });

    throw error;
  }
}

Gesundheitsprüfungen

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;
  }
}

📱 Mobile & Frontend-Integration

React Hook Beispiel

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('Benachrichtigung konnte nicht gesendet werden');
      }

      const result = await response.json();
      return result;
    } catch (err) {
      setError(err.message);
      throw err;
    } finally {
      setLoading(false);
    }
  };

  return { sendNotification, loading, error };
}

🔧 Konfigurationsverwaltung

Umgebungskonfiguration

// 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
  }
};

Client-Konfiguration

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);
  }
}

📚 Dokumentation & Team-Richtlinien

Code-Dokumentation

/**
 * WhatsApp-Benachrichtigung mit Template senden
 *
 * @param {Object} notification - Benachrichtigungsdaten
 * @param {string} notification.phoneNumber - Telefonnummer des Empfängers (internationales Format, ohne +)
 * @param {string} notification.templateName - Name der genehmigten Vorlage
 * @param {string} notification.language - Sprachcode (z. B. 'de_DE', 'en_US')
 * @param {Object} notification.variables - Template-Variablen
 * @param {string} [notification.scheduledTime] - ISO 8601 Datum/Uhrzeit für Planung
 * @param {string} [notification.timezone] - IANA Zeitzonen-Code
 *
 * @returns {Promise<Object>} API-Antwort mit notification_id
 *
 * @throws {ValidationError} Wenn erforderliche Parameter fehlen
 * @throws {APIError} Wenn API-Aufruf fehlschlägt
 *
 * @example
 * const result = await sendWhatsAppNotification({
 *   phoneNumber: '34666033135',
 *   templateName: 'welcome_user',
 *   language: 'de_DE',
 *   variables: { name: 'Hans', course: 'JavaScript' }
 * });
 */
async function sendWhatsAppNotification(notification) {
  // Implementierung...
}

Team-Standards

API-Integrations-Checkliste:
  • ✅ Korrekte Fehlerbehandlung implementieren
  • ✅ Anfrage-/Antwort-Logging hinzufügen
  • ✅ Unit-Tests einschließen
  • ✅ Eingabeparameter validieren
  • ✅ Ratenbegrenzung behandeln
  • ✅ Umgebungsspezifische Konfigurationen verwenden
  • ✅ Alle Funktionen und Klassen dokumentieren
  • ✅ Gesundheitsprüfungen implementieren
  • ✅ Monitoring und Alarme hinzufügen

🚨 Häufige Fallstricke & Lösungen

Fallstrick: Hartcodierte Werte

// ❌ Schlecht
const templateName = 'welcome_template_final_v2';

// ✅ Gut
const templateName = config.templates.welcome;

Fallstrick: Schlechte Fehlerbehandlung

// ❌ Schlecht
try {
  await sendNotification(data);
} catch (error) {
  console.log('Error:', error);
}

// ✅ Gut
try {
  await sendNotification(data);
} catch (error) {
  logger.error('Benachrichtigung fehlgeschlagen', {
    error: error.message,
    data: sanitizeForLog(data),
    stack: error.stack
  });

  throw new NotificationError(
    'Benachrichtigung konnte nicht gesendet werden',
    error.statusCode,
    error.requestId
  );
}

Fallstrick: Ratenlimits ignorieren

// ❌ Schlecht - Alles auf einmal senden
notifications.forEach(notification => {
  sendNotification(notification);
});

// ✅ Gut - Ratenlimits respektieren
await sendNotificationsBatch(notifications, 10);
Denken Sie daran: Die Befolgung dieser Best Practices hilft Ihnen, zuverlässigere, sicherere und wartbarere Integrationen mit der Lovi API zu erstellen.