Volver al inicio

Integración de Next.js con 1C Bitrix: Transferencia de leads asíncrona

Guía práctica de integración asíncrona de Next.js 16 con 1C Bitrix. Usando after() para procesamiento de leads en segundo plano, configuración de mecanismos de reintento y esquema de PostgreSQL. Resolviendo problemas de timeout y duplicados.

Next.js y 1C Bitrix: cómo enviar leads sin colas ni workers
Advertisement 728x90

Transferencia asíncrona de leads desde Next.js a 1C-Bitrix: Un patrón sin colas ni workers

El envío síncrono de leads al CRM vía REST API provoca problemas críticos: el usuario espera la respuesta hasta que se completa la solicitud externa, y la inestabilidad de la API de 1C-Bitrix genera timeouts y errores. Proponemos una solución en Next.js 16 que elimina colas y procesos en segundo plano. La implementación guarda el lead en una base de datos PostgreSQL local, responde instantáneamente al usuario y maneja la integración con Bitrix mediante after() —un mecanismo para procesamiento en segundo plano después de enviar la respuesta HTTP.

Arquitectura sin colas: Tres capas de responsabilidad

Principio clave: separar las operaciones en partes síncronas y asíncronas. Al recibir el formulario:

  • Validación de datos mediante zod
  • Guardar en PostgreSQL con bitrix_id=NULL
  • Respuesta instantánea 200 OK
  • Envío en segundo plano a Bitrix mediante after()

Esto garantiza:

Google AdInline article slot
  • Independencia de la estabilidad del REST API de Bitrix
  • Sin bloqueo de la interfaz de usuario
  • Posibilidad de procesar manualmente los leads no entregados mediante SELECT * FROM leads WHERE bitrix_id IS NULL

La característica clave del enfoque es prescindir de Redis/BullMQ. Todas las operaciones caben en una ruta API de Next.js, usando el after() integrado para el procesamiento posterior a la respuesta. Esto reduce la complejidad de la infraestructura y simplifica el despliegue en VPS mediante systemd/nginx.

after() en Next.js 16: Implementación técnica

El método after() resuelve el problema del "dispara y olvida" en entornos sin servidor. A diferencia de Promise.resolve().then(), asegura la ejecución de la tarea incluso después de enviar la respuesta, sin terminar el proceso hasta que la operación se complete. Ejemplo de procesamiento de formulario:

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

Matizes clave:

Google AdInline article slot
  • Antidúplicación implementada mediante una ventana de 10 minutos en PostgreSQL, no a través de Bitrix (el CRM acepta duplicados sin advertencia)
  • bitrix_id almacenado como text nullable —refleja la naturaleza asíncrona de la operación
  • Errores de integración registrados en journalctl, sin afectar la respuesta al usuario

Cliente HTTP confiable para 1C-Bitrix

Parámetros críticos para un trabajo estable con una API inestable:

  • MAX_ATTEMPTS = 2 —equilibrio entre fiabilidad y carga
  • REQUEST_TIMEOUT_MS = 8000 —por encima de la latencia p95 de Bitrix (400-700 ms)
  • RETRY_DELAY_MS = 1500 —backoff lineal sin complejidad exponencial

La implementación del cliente incluye:

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

Tres puntos clave:

Google AdInline article slot
  • Análisis manual mediante response.text() en lugar de response.json() —evita errores de Bitrix con respuestas vacías
  • Manejo de dos tipos de errores: estados HTTP y errores internos del CRM
  • Limpieza garantizada del temporizador en finally —previene fugas de memoria

Características de autenticación y esquema de datos

Para la integración, se elige webhook entrante en lugar de OAuth:

  • Sin dependencia del Marketplace y aprobaciones
  • Gestión simple de la variable de entorno BITRIX_WEBHOOK_URL
  • Scopes configurados en el panel de administración de Bitrix

El riesgo de filtración de token en la URL se minimiza con la regla: logs y código muestran solo el método API, no la URL completa.

Esquema de la tabla 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()
);

¿Por qué bitrix_id text, no integer?

  • Bitrix devuelve el ID como string en JSON
  • Sin necesidad de conversión de tipos
  • UUID en PostgreSQL asegura idempotencia

Puntos clave

  • Fuente única de verdad —BD local: el lead se guarda antes de contactar a Bitrix, asegurando los datos incluso en fallo del CRM
  • Procesamiento posterior a la respuesta: after() reemplaza las colas, eliminando complejidad de infraestructura
  • Manejo de errores: reintentos con timeouts y logging en journalctl aseguran observabilidad
  • Antidúplicación a nivel de BD: ventana de 10 minutos por teléfono previene duplicados independientemente del CRM
  • Autenticación segura: webhook requiere control estricto del logging de URL

Este patrón es adecuado para proyectos con hasta 50 solicitudes por minuto. Para sistemas de alta carga, agregar monitoreo mediante SELECT COUNT(*) FROM leads WHERE bitrix_id IS NULL y reintentos automáticos vía cron. En la implementación actual, el manejo manual de errores es preferible a los reintentos automáticos —reduce el riesgo de duplicación de leads durante fallos temporales.

— Editorial Team

Advertisement 728x90

Leer después