Asynchrone Lead-Übertragung von Next.js zu 1C-Bitrix: Ein warteschlangen- und workerfreies Muster
Synchrone Lead-Übermittlung an das CRM über REST API verursacht kritische Probleme: Der Nutzer wartet auf eine Antwort, bis die externe Anfrage abgeschlossen ist, und die Instabilität der 1C-Bitrix API führt zu Timeouts und Fehlern. Wir schlagen eine Lösung auf Next.js 16 vor, die Queues und Hintergrundprozesse eliminiert. Die Implementierung speichert den Lead in einer lokalen PostgreSQL-Datenbank, antwortet dem Nutzer sofort und kümmert sich um die Integration mit Bitrix über after() – ein Mechanismus für Hintergrundverarbeitung nach dem Senden der HTTP-Antwort.
Warteschlangenfreie Architektur: Drei Schichten der Verantwortung
Grundsatzprinzip – Trennung der Operationen in synchrone und asynchrone Teile. Bei Erhalt des Formulars:
- Datenvalidierung über zod
- Speichern in PostgreSQL mit bitrix_id=NULL
- Sofortige 200 OK-Antwort
- Hintergrundversand an Bitrix über after()
Das garantiert:
- Unabhängigkeit von der Stabilität der Bitrix REST API
- Keine Blockierung der Benutzeroberfläche
- Möglichkeit, nicht zugestellte Leads manuell über SELECT * FROM leads WHERE bitrix_id IS NULL zu verarbeiten
Das entscheidende Merkmal des Ansatzes ist der Verzicht auf Redis/BullMQ. Alle Operationen passen in eine Next.js API-Route, die die integrierte after() für die Nachbearbeitung nach der Antwort nutzt. Das reduziert die Komplexität der Infrastruktur und vereinfacht den Deployment auf VPS über systemd/nginx.
after() in Next.js 16: Technische Implementierung
Die after()-Methode löst das „fire-and-forget“-Problem in serverlosen Umgebungen. Im Gegensatz zu Promise.resolve().then() stellt sie sicher, dass die Aufgabe auch nach dem Senden der Antwort ausgeführt wird, ohne den Prozess vor Abschluss der Operation zu beenden. Beispiel für Formularverarbeitung:
export async function POST(request: Request) {
// ... validatsiya and antidubl
const [lead] = await pgQuery(...);
after(async () => {
try {
const bitrixId = await sendToBitrix24(payload);
await pgQuery(
`UPDATE leads SET bitrix_id = $1 WHERE id = $2`,
[bitrixId, lead.id]
);
} catch (error) {
console.error("[Leads API] Error otpravki in Bitriks:", error);
}
});
return Response.json({ ok: true, id: lead.id });
}
Wichtige Nuancen:
- Anti-Duplikation über ein 10-Minuten-Fenster in PostgreSQL implementiert, nicht über Bitrix (CRM akzeptiert Duplikate ohne Warnung)
- bitrix_id als nullable text gespeichert – spiegelt die asynchrone Natur der Operation wider
- Integrationsfehler in journalctl protokolliert, ohne die Nutzerantwort zu beeinflussen
Zuverlässiger HTTP-Client für 1C-Bitrix
Kritische Parameter für stabilen Betrieb mit einer instabilen API:
- MAX_ATTEMPTS = 2 – Balance zwischen Zuverlässigkeit und Last
- REQUEST_TIMEOUT_MS = 8000 – über Bitrix p95-Latenz (400-700 ms)
- RETRY_DELAY_MS = 1500 – linearer Backoff ohne exponentielle Komplexität
Client-Implementierung umfasst:
async function bitrixRequest(method: string, payload: Record<string, unknown>) {
for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
try {
const response = await fetch(url, {
signal: controller.signal,
// ...
});
const text = await response.text();
const json = text ? JSON.parse(text) : {};
if (!response.ok || json.error) {
throw new Error(json.error_description || `HTTP ${response.status}`);
}
return json;
} catch (error) {
if (attempt >= MAX_ATTEMPTS) throw error;
await new Promise(r => setTimeout(r, RETRY_DELAY_MS));
} finally {
clearTimeout(timer);
}
}
}
Drei Schlüsselpunkte:
- Manuelles Parsen über response.text() statt response.json() – umgeht Bitrix-Fehler mit leeren Antworten
- Behandlung zweier Fehlertypen: HTTP-Status und interne CRM-Fehler
- Garantierte Timer-Bereinigung in finally – verhindert Speicherlecks
Authentifizierungsmerkmale und Datenschema
Für die Integration wird ein eingehender Webhook statt OAuth gewählt:
- Keine Abhängigkeit von Marketplace und Genehmigungen
- Einfache Verwaltung der Umgebungsvariable BITRIX_WEBHOOK_URL
- Scopes im Bitrix-Adminpanel konfiguriert
Risiko der Token-Freigabe in der URL minimiert durch Regel: Logs und Code zeigen nur die API-Methode, nicht die volle URL.
Tabellenschema für leads:
CREATE TABLE leads (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
name text NOT NULL,
phone text NOT NULL,
source text,
bitrix_id text,
created_at timestamptz NOT NULL DEFAULT now()
);
Warum bitrix_id als text, nicht integer?
- Bitrix liefert ID als String im JSON
- Keine Typkonvertierung nötig
- UUID in PostgreSQL gewährleistet Idempotenz
Wichtige Punkte
- Single source of truth – lokale DB: Lead vor Kontakt zu Bitrix gespeichert, gewährleistet Daten auch bei CRM-Ausfall
- Nach-Antwort-Verarbeitung: after() ersetzt Queues und eliminiert Infrastrukturkomplexität
- Fehlerbehandlung: Retries mit Timeouts und journalctl-Logging sorgen für Beobachtbarkeit
- DB-basierte Anti-Duplikation: 10-Minuten-Fenster nach Telefon verhindert Duplikate unabhängig vom CRM
- Sichere Authentifizierung: Webhook erfordert strenge URL-Logging-Kontrolle
Dieses Muster eignet sich für Projekte mit bis zu 50 Anfragen pro Minute. Für Systeme mit hoher Last Monitoring über SELECT COUNT(*) FROM leads WHERE bitrix_id IS NULL hinzufügen und automatische Retries über cron. In der aktuellen Implementierung ist manuelle Fehlerbehandlung vorzuziehen vor automatischen Retries – reduziert das Risiko von Lead-Duplikaten bei temporären Ausfällen.
— Editorial Team
Noch keine Kommentare.