BookinglyTech News
Infraestructura

Upyo presenta una librería de email universal para Node.js, Deno, Bun y edge runtimes

La librería @upyo/core permite enviar correo sin depender de módulos específicos de Node.js, usando la API fetch y Web Streams, y soporta SMTP, JMAP y APIs de proveedores como SES, SendGrid y Resend.

2 min de lecturaLobsters0 vistas

¿Qué es Upyo?

Upyo es una librería JavaScript/TypeScript que abstrae el envío de correo electrónico a través de un único Transport‑interface. Se basa en APIs web estándar—fetch(), Web Streams y Web Crypto—para que el mismo código funcione en Node.js, Deno, Bun y runtimes edge como Cloudflare Workers o Vercel Edge Runtime.

Compatibilidad y proveedores

El núcleo de Upyo expone tres métodos: send(), sendMany() y soporte para AbortSignal. Cada transporte implementa esta interfaz y puede comunicarse con cualquier proveedor: SMTP, JMAP, Resend, SendGrid, Mailgun, Amazon SES, Plunk y Lettermint. Para SMTP, el paquete @upyo/smtp incluye su propia implementación de la firma AWS Signature v4, evitando la dependencia del SDK de AWS que solo funciona en Node.js.

Separación de responsabilidades

Al encapsular la lógica de reintentos en RetryTransport y la gestión de balanceo en PoolTransport, el código que dispara el envío no necesita conocer detalles del proveedor ni la estrategia de reintentos. Por ejemplo:

import { MockTransport } from "@upyo/mock";
import { RetryTransport } from "@upyo/retry";
const provider = new MockTransport();
const transport = new RetryTransport(provider, {
  maxAttempts: 3,
  backoff: { baseDelayMilliseconds: 1000, maxDelayMilliseconds: 30000, factor: 2 },
});

El RetryTransport devuelve un Receipt con campos estructurados (retryable, category, retryAfterMilliseconds), lo que permite decidir uniformemente si se reintenta una 429 de Resend o un error SMTP transitorio.

Desarrollo sin envíos reales

Para evitar abrir bandejas de entrada durante pruebas, Upyo ofrece LogTapeTransport, que registra los mensajes en lugar de enviarlos. En producción se puede inyectar un transporte real, como SmtpTransport:

import { LogTapeTransport } from "@upyo/logtape";
import { SmtpTransport } from "@upyo/smtp";
const smtp = new SmtpTransport({
  host: "smtp.example.com",
  port: 587,
  auth: { user: "smtp-user", pass: "smtp-password" },
});
const transport = process.env.NODE_ENV === "development"
  ? new LogTapeTransport({ category: ["app", "email"] })
  : new LogTapeTransport({ transport: smtp, category: ["app", "email"] });

Ejemplo práctico: magic‑link

El patrón de login por magic link se adapta fácilmente:

import { createMessage } from "@upyo/core";
import type { Transport } from "@upyo/core";
async function sendMagicLink(transport: Transport, email: string, link: string) {
  await transport.send(createMessage({
    from: "auth@example.com",
    to: email,
    subject: "Your login link",
    content: { text: `Click to log in: ${link}` },
  }));
}

El transporte que se pasa puede ser LogTapeTransport en dev o SmtpTransport en prod, sin que la función cambie.

Por qué importa

Con la fragmentación de entornos de ejecución y la necesidad de cambiar de proveedor entre dev y prod, una librería que mantiene el mismo API de envío y permite intercambiar transportes sin tocar la lógica de negocio reduce drásticamente la deuda técnica. Además, al no depender de módulos de Node.js, se evita el problema de incompatibilidad en runtimes edge.

Enlaces útiles

Repositorio en GitHub – Código fuente y documentación oficial. Paquete en npm – Instalación y ejemplos.