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.
¿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.


