Skip to content

Why Upyo?

Upyo[1] is a simple and modern email library that works across multiple runtimes including Node.js, Deno, Bun, and edge functions. It provides a universal interface for email delivery, making it easy to send emails with minimal setup.

Cross-runtime compatibility

Upyo is designed to work seamlessly across different JavaScript runtimes. Whether you're using Node.js, Deno, Bun, or deploying to edge functions, Upyo provides a consistent API for sending emails. This means you can write your email sending code once and run it anywhere without worrying about runtime-specific details.

Lightweight and dependency-free

Upyo has zero dependencies, making it lightweight and easy to integrate into your projects. You don't have to worry about managing additional packages or bloat. Upyo is designed to be minimalistic, focusing solely on email delivery without unnecessary complexity.

Dead simple API

Upyo provides a straightforward and intuitive API for sending emails. You can send emails with just a few lines of code, without needing to understand complex configurations or setups. The API is designed to be easy to use, so you can focus on building your application rather than dealing with email delivery intricacies.

Here's a quick example of sending an email with Upyo:

typescript
import { 
createMessage
} from "@upyo/core";
import {
MailgunTransport
} from "@upyo/mailgun";
import
process
from "node:process";
const
message
=
createMessage
({
from
: "[email protected]",
to
: "[email protected]",
subject
: "Hello from Upyo!",
content
: {
text
: "This is a test email." },
}); const
transport
= new
MailgunTransport
({
apiKey
:
process
.
env
.
MAILGUN_KEY
!,
domain
:
process
.
env
.
MAILGUN_DOMAIN
!,
region
:
process
.
env
.
MAILGUN_REGION
as "us" | "eu",
}); const
receipt
= await
transport
.
send
(
message
);
if (
receipt
.
successful
) {
console
.
log
("Message sent with ID:",
receipt
.
messageId
);
} else {
console
.
error
("Send failed:",
receipt
.
errorMessages
.
join
(", "));
}

Built for testing

Upyo is designed with testing in mind. The @upyo/mock transport provides a comprehensive testing solution that lets you verify email functionality without sending real emails. You can inspect sent messages, simulate network delays and failures, and test complex async email workflows with confidence.

The mock transport implements the same interface as real transports, making it a drop-in replacement for testing. This means you can write reliable tests that verify your email logic works correctly across all scenarios:

typescript
import { 
createMessage
} from "@upyo/core";
import {
MockTransport
} from "@upyo/mock";
// Use mock transport in tests - same interface, no real emails const
transport
= new
MockTransport
();
const
message
=
createMessage
({
from
: "[email protected]",
to
: "[email protected]",
subject
: "Test Email",
content
: {
text
: "This is a test." },
}); const
receipt
= await
transport
.
send
(
message
);
// Verify the email was "sent" successfully
console
.
log
(
receipt
.
successful
); // true
// Inspect what was sent for testing assertions const
sentMessages
=
transport
.
getSentMessages
();
console
.
log
(
sentMessages
[0].
subject
); // "Test Email"
console
.
log
(
sentMessages
[0].
recipients
[0].
address
); // "[email protected]"

Provider independence

Upyo's transport abstraction means you're never locked into a single email service provider. Whether you use SMTP, Mailgun, SendGrid, Amazon SES, or any future provider, your application code stays exactly the same. Switch providers in minutes, not days:

typescript
import { 
createMessage
} from "@upyo/core";
import {
SmtpTransport
} from "@upyo/smtp";
import {
MailgunTransport
} from "@upyo/mailgun";
const
message
=
createMessage
({
from
: "[email protected]",
to
: "[email protected]",
subject
: "Hello from Upyo!",
content
: {
text
: "This works with any transport!" },
}); // Start with SMTP for development const
smtpTransport
= new
SmtpTransport
({
host
: "localhost",
port
: 1025,
}); // Switch to Mailgun for production - same interface! const
mailgunTransport
= new
MailgunTransport
({
apiKey
: "your-api-key",
domain
: "your-domain.com",
}); // Your application code never changes async function
sendEmail
(
transport
: any) {
const
receipt
= await
transport
.send(
message
);
return
receipt
.successful ?
receipt
.messageId :
receipt
.errorMessages;
} // Works identically with any transport await
sendEmail
(
smtpTransport
); // ✅ Works
await
sendEmail
(
mailgunTransport
); // ✅ Works

Observability

Upyo integrates seamlessly with OpenTelemetry to provide comprehensive observability for your email operations. Monitor delivery rates, track performance, and debug issues with distributed tracing—all without changing your existing code:

typescript
import { 
createMessage
} from "@upyo/core";
import {
SmtpTransport
} from "@upyo/smtp";
import {
createOpenTelemetryTransport
} from "@upyo/opentelemetry";
// Wrap any transport with OpenTelemetry instrumentation const
baseTransport
= new
SmtpTransport
({
host
: "smtp.example.com" });
const
transport
=
createOpenTelemetryTransport
(
baseTransport
, {
serviceName
: "email-service",
tracing
: {
enabled
: true },
metrics
: {
enabled
: true },
}); // Your email code stays exactly the same const
message
=
createMessage
({
from
: "[email protected]",
to
: "[email protected]",
subject
: "Production Email",
content
: {
text
: "Now with full observability!" },
}); await
transport
.
send
(
message
);
// Automatically creates traces and records metrics: // - Email delivery success/failure rates // - Send operation latency histograms // - Error classification by type // - Distributed tracing for debugging

Key observability features:

Zero-code instrumentation
Add observability to any transport without modifying your email logic
Comprehensive metric
Track delivery rates, latency, batch sizes, and error distributions
Distributed tracing
Follow email operations across your entire system with OpenTelemetry spans
Smart error classification
Automatically categorize failures (auth, network, validation, etc.) for better alerting
Production-tested
Built on OpenTelemetry standards used by major observability platforms

Whether you're using Jaeger, Prometheus, Grafana, or commercial APM solutions, Upyo's OpenTelemetry support ensures you have the insights needed to run email services reliably at scale.


  1. Upyo (pronounced /oo-pee-oh/) comes from the Sino-Korean word 郵票 (upyo), meaning postage stamp. The name reflects the library's purpose: just as postage stamps enable mail delivery across different postal systems, Upyo enables email delivery across different runtime environments and service providers. ↩︎