SOP: conectar un formulario de Nuxt con MailerLite sin reemplazar SendGrid
Conecta un formulario de Nuxt con MailerLite sin perder el flujo de SendGrid: secretos solo en servidor, campos aprobados y manejo seguro de fallos.
Una integración de Nuxt con MailerLite no debe reemplazar un flujo de formularios que ya funciona con SendGrid. El patrón seguro mantiene a SendGrid como responsable de la entrega transaccional y permite que el servidor de Nuxt cree o actualice un suscriptor de MailerLite solo para los formularios aprobados expresamente.
Esta separación protege las API keys, evita que formularios de contacto no relacionados entren a una automatización de marketing y permite comunicar correctamente un éxito parcial. El SOP aplica a aplicaciones Nuxt desplegadas en Vercel, tanto proyectos Nuxt 3 en mantenimiento como implementaciones nuevas con Nuxt 4.
Respuesta corta
Formulario Vue seleccionado
→ POST /api/forms/submit
→ validar y normalizar una vez
→ entrega por SendGrid o fallback existente
→ upsert opcional de suscriptor + grupo en MailerLite
→ responder según la finalidad principal del formulario
El navegador nunca recibe el token de MailerLite. Solo el servidor de Nuxt llama a la API y solo un formType registrado puede hacerlo. Es el mismo principio que protege otras integraciones de formularios con secretos privados, como el flujo de SendGrid y Orbitype.
SendGrid y MailerLite cumplen funciones distintas
Las dos plataformas envían correo, pero no deberían modelarse como una misma cadena.
| Servicio | Responsabilidad en este flujo |
|---|---|
| SendGrid | Entregar la notificación del lead y otros mensajes transaccionales inmediatos. |
| MailerLite | Mantener datos de suscriptores, grupos y automatizaciones de marketing. |
| Ruta de Nuxt | Validar el formulario, coordinar adaptadores y aplicar la política de fallos. |
MailerLite puede iniciar una automatización cuando un suscriptor entra a un grupo. Así, la pertenencia al grupo funciona como trigger sin acoplar la distribución de campañas a la aplicación. La guía oficial de automation triggers aclara también que la reentrada depende de la configuración del workflow y que una persona ya presente en el grupo debe salir antes de volver a entrar.
Usa la API para un envío saliente desde el sitio
El sitio necesita enviar datos a MailerLite, por lo que el servidor de Nuxt hace una solicitud REST saliente. Un webhook de MailerLite es el recorrido inverso: MailerLite avisa a tu aplicación sobre un evento ocurrido dentro de la plataforma. No sirve para crear o actualizar un suscriptor desde un formulario web.
La operación relevante es:
POST https://connect.mailerlite.com/api/subscribers
Authorization: Bearer <token-solo-servidor>
Content-Type: application/json
Accept: application/json
MailerLite documenta este endpoint como una operación de creación o actualización. Para un contacto existente, la operación no elimina campos o grupos omitidos; los IDs de grupo enviados añaden membresía. Consulta el contrato vigente en la referencia oficial de Subscribers.
1. Define qué significa éxito antes de programar
El endpoint no debería devolver el mismo resultado ante un fallo para todos los formularios. Primero define la operación principal.
| Política | Operación principal | Si falla MailerLite | Si falla SendGrid |
|---|---|---|---|
contact | Entregar una solicitud | Registrar el fallo secundario y conservar el éxito. | Mostrar error. |
newsletter | Crear la suscripción | No afirmar que se completó la suscripción. | Normalmente no aplica. |
mixed | Entregar un lead e iniciar seguimiento | Conservar el éxito del lead y registrar el fallo de sincronización. | Mostrar error. |
Un formulario para descargar un recurso suele usar mixed: la solicitud ya se procesó al completarse la entrega transaccional, mientras MailerLite habilita el workflow posterior. En cambio, un newsletter puro promete una suscripción y debe mostrar un fallo de MailerLite en lugar de afirmar éxito.
2. Guarda el token en runtime configuration privada de Nuxt
No expongas el token mediante runtimeConfig.public, una variable NUXT_PUBLIC_*, un componente Vue, contenido del CMS, una solicitud del navegador o un fixture versionado.
// nuxt.config.ts
export default defineNuxtConfig({
runtimeConfig: {
mailerliteApiKey: "",
},
})
# .env.example — placeholder solo para servidor
NUXT_MAILERLITE_API_KEY="your-mailerlite-api-token"
Nuxt conserva en el servidor las claves declaradas directamente en runtimeConfig; las ubicadas bajo runtimeConfig.public están disponibles para código cliente. Revisa la guía de runtime configuration de Nuxt antes de definir los nombres y overrides de entorno.
En Vercel, configura el valor en los entornos Development, Preview y Production necesarios. Los cambios de variables aplican a deployments nuevos, no a uno existente, así que hay que redeplegar. La guía de variables de entorno de Vercel explica ese comportamiento.
3. Usa un registro por formulario como lista blanca
La API key es secreta. Un ID de grupo es configuración, pero también debe ser explícito: une un formulario a una automatización y determina qué campos pueden salir de la aplicación.
// server/utils/mailerliteFormConfig.ts
export type MailerLiteFailurePolicy = "contact" | "newsletter" | "mixed"
export type MailerLiteFormConfig = {
groupId: string
groupName?: string
emailField: string
fieldMapping: Record<string, string>
consentField?: string
failurePolicy: MailerLiteFailurePolicy
}
const MAILERLITE_FORMS = {
lead_magnet: {
groupId: "YOUR_MAILERLITE_GROUP_ID",
groupName: "Lead Magnet — PDF Guide",
emailField: "email",
fieldMapping: {
firstName: "name",
lastName: "last_name",
phone: "phone",
},
failurePolicy: "mixed",
},
} satisfies Record<string, MailerLiteFormConfig>
export function getMailerliteFormConfig(formType: string) {
return MAILERLITE_FORMS[formType as keyof typeof MAILERLITE_FORMS]
}
El registro aporta cuatro garantías: un formulario sin entrada no hace llamadas a MailerLite; cada registro tiene su grupo y política; fieldMapping funciona como whitelist; y las reglas de consentimiento quedan visibles junto a la integración. Crea primero los custom fields en MailerLite y utiliza después sus nombres internos en el mapping.
4. Mantén pequeño el cliente de MailerLite y solo en servidor
El cliente HTTP no debe conocer componentes Vue, nombres de formularios ni lógica de SendGrid. Debe devolver un resultado seguro y dejar que el endpoint decida el mensaje para la persona usuaria.
const MAILERLITE_SUBSCRIBERS_URL =
"https://connect.mailerlite.com/api/subscribers"
export async function upsertSubscriberToGroups({
apiKey,
email,
fields,
groupIds,
timeoutMs = 10_000,
}: {
apiKey: string
email: string
fields?: Record<string, string>
groupIds: string[]
timeoutMs?: number
}) {
const controller = new AbortController()
const timeout = setTimeout(() => controller.abort(), timeoutMs)
try {
const response = await fetch(MAILERLITE_SUBSCRIBERS_URL, {
method: "POST",
headers: {
Authorization: `Bearer ${apiKey}`,
"Content-Type": "application/json",
Accept: "application/json",
},
body: JSON.stringify({ email, fields, groups: groupIds }),
signal: controller.signal,
})
return response.ok
? { ok: true as const, status: response.status }
: { ok: false as const, status: response.status }
} catch {
return { ok: false as const }
} finally {
clearTimeout(timeout)
}
}
No fuerces status: "active" ni resubscribe: true como comportamiento predeterminado. Reactivar a una persona dada de baja, rebotada o marcada como junk es una decisión de consentimiento y producto. Tampoco registres tokens, correos, bodies ni respuestas completas del proveedor.
5. Coordina los dos adaptadores desde el endpoint
Valida el envío una sola vez, realiza la operación primaria y luego evalúa la integración opcional.
const fields = validateAndNormalizeFormSubmission(body)
const delivery = await sendTransactionalForm({
formType: body.formType,
fields,
})
if (!delivery.ok) {
throw createError({ statusCode: 502, statusMessage: "Inténtalo de nuevo." })
}
const config = getMailerliteFormConfig(body.formType)
const email = config ? fields[config.emailField] : undefined
if (config && email && hasMailerliteConsent(fields, config)) {
const result = await upsertSubscriberToGroups({
apiKey: useRuntimeConfig(event).mailerliteApiKey,
email,
fields: buildMailerliteFields(fields, config.fieldMapping),
groupIds: [config.groupId],
})
if (!result.ok && config.failurePolicy === "newsletter") {
throw createError({ statusCode: 502, statusMessage: "No se pudo completar la suscripción." })
}
}
return { ok: true }
Para un formulario mixed, la secuencia conserva un éxito parcial controlado: SendGrid ya entregó el lead, la aplicación registra de forma segura un fallo de MailerLite si ocurre y la persona no recibe una señal que la invite a duplicar su envío. Si hace falta recuperación, usa una cola o reintentos persistentes, no varios reintentos síncronos dentro del handler.
6. Trata consentimiento y mapeo de datos como decisiones de producto
La tecnología no puede decidir si hay consentimiento. Una automatización puede formar parte de la entrega de un recurso solicitado, mientras una campaña de marketing más amplia puede requerir una aceptación distinta.
Cuando se requiere checkbox, decláralo en el registro:
lead_magnet: {
// resto de la configuración
consentField: "marketingConsent",
failurePolicy: "mixed",
}
Normaliza el valor y compruébalo en el servidor. Es un patrón técnico, no asesoría legal: los textos del formulario, contenido de la automatización, mercados y política de privacidad requieren su revisión correspondiente.
7. Prueba el comportamiento sin tocar datos de producción
Los unit tests deben mockear fetch; una prueba automática no puede crear suscriptores reales. Como mínimo, cubre estos casos:
- un
formTypesin registro no llama a MailerLite; - el payload contiene únicamente campos aprobados;
- el consentimiento acepta y rechaza valores normalizados según lo esperado;
- URL, Bearer header e ID de grupo son correctos;
200y201cuentan como éxito;- falta de configuración, timeout,
422y429producen fallos controlados; - un fallo de MailerLite conserva la respuesta exitosa de un formulario
mixed; - un fallo de SendGrid conserva el error de entrega principal.
Haz un smoke test real solo como operación manual y explícita con una dirección autorizada. Confirma el grupo después y elimina datos de prueba cuando corresponda. No ejecutes esa prueba en unit tests, pull requests ni hooks de deployment.
Checklist de implementación
- Solo los formularios registrados pueden llamar a MailerLite.
- SendGrid conserva el papel de adaptador transaccional.
- El token de API está en configuración privada del servidor.
- Grupos, whitelists de campos y políticas viven en el registro.
-
statusyresubscribese omiten salvo aprobación expresa. - El consentimiento coincide con la finalidad del formulario.
- Los unit tests simulan las solicitudes externas.
- Preview y producción tienen sus valores configurados y redeplegados.
- Los logs excluyen tokens y datos personales innecesarios.
El aprendizaje reutilizable no es la llamada a fetch. Es volver explícitos la finalidad de cada formulario, los datos permitidos, el consentimiento y la política de fallos. Con esa estructura, SendGrid mantiene la transacción que ya domina y MailerLite añade el workflow del suscriptor seleccionado sin convertirse en una dependencia accidental de todos los formularios.