How to Send Emails in Bun (2026 Guide)

Nik@nikpolale
10 min read
Bun is a fast JavaScript runtime with built-in fetch, a native HTTP server, and npm compatibility. Email sending is one HTTP call. Install packages with bun add and they just work.
This guide covers email sending with Bun's native server and the Elysia framework.
Install
Terminal
bun add sequenzyTerminal
bun add resendTerminal
bun add @sendgrid/mailCreate an Email Client
src/email.ts
import Sequenzy from "sequenzy";
export const sequenzy = new Sequenzy();
// Reads SEQUENZY_API_KEY from Bun.env automaticallysrc/email.ts
import { Resend } from "resend";
export const resend = new Resend(Bun.env.RESEND_API_KEY);src/email.ts
import sgMail from "@sendgrid/mail";
sgMail.setApiKey(Bun.env.SENDGRID_API_KEY!);
export { sgMail };Send with Bun.serve
src/index.ts
import { sequenzy } from "./email";
Bun.serve({
port: 3000,
async fetch(req) {
const url = new URL(req.url);
if (req.method === "POST" && url.pathname === "/api/send-welcome") {
const { email, name } = await req.json();
if (!email || !name) {
return Response.json({ error: "email and name required" }, { status: 400 });
}
try {
const result = await sequenzy.transactional.send({
to: email,
subject: `Welcome, ${name}`,
body: `<h1>Welcome, ${name}</h1><p>Your account is ready.</p>`,
});
return Response.json({ jobId: result.jobId });
} catch {
return Response.json({ error: "Failed to send" }, { status: 500 });
}
}
return new Response("Not found", { status: 404 });
},
});
console.log("Server running on port 3000");src/index.ts
import { resend } from "./email";
Bun.serve({
port: 3000,
async fetch(req) {
const url = new URL(req.url);
if (req.method === "POST" && url.pathname === "/api/send-welcome") {
const { email, name } = await req.json();
if (!email || !name) {
return Response.json({ error: "email and name required" }, { status: 400 });
}
const { data, error } = await resend.emails.send({
from: "Your App <noreply@yourdomain.com>",
to: email,
subject: `Welcome, ${name}`,
html: `<h1>Welcome, ${name}</h1><p>Your account is ready.</p>`,
});
if (error) {
return Response.json({ error: error.message }, { status: 500 });
}
return Response.json({ id: data?.id });
}
return new Response("Not found", { status: 404 });
},
});
console.log("Server running on port 3000");src/index.ts
import { sgMail } from "./email";
Bun.serve({
port: 3000,
async fetch(req) {
const url = new URL(req.url);
if (req.method === "POST" && url.pathname === "/api/send-welcome") {
const { email, name } = await req.json();
if (!email || !name) {
return Response.json({ error: "email and name required" }, { status: 400 });
}
try {
await sgMail.send({
to: email,
from: "noreply@yourdomain.com",
subject: `Welcome, ${name}`,
html: `<h1>Welcome, ${name}</h1><p>Your account is ready.</p>`,
});
return Response.json({ sent: true });
} catch {
return Response.json({ error: "Failed to send" }, { status: 500 });
}
}
return new Response("Not found", { status: 404 });
},
});
console.log("Server running on port 3000");Run with:
bun run src/index.tsSend with Elysia
bun add elysiasrc/index.ts
import { Elysia, t } from "elysia";
import { sequenzy } from "./email";
new Elysia()
.post("/api/send-welcome", async ({ body }) => {
const result = await sequenzy.transactional.send({
to: body.email,
subject: `Welcome, ${body.name}`,
body: `<h1>Welcome, ${body.name}</h1><p>Your account is ready.</p>`,
});
return { jobId: result.jobId };
}, {
body: t.Object({
email: t.String({ format: "email" }),
name: t.String(),
}),
})
.listen(3000);
console.log("Server running on port 3000");src/index.ts
import { Elysia, t } from "elysia";
import { resend } from "./email";
new Elysia()
.post("/api/send-welcome", async ({ body }) => {
const { data, error } = await resend.emails.send({
from: "Your App <noreply@yourdomain.com>",
to: body.email,
subject: `Welcome, ${body.name}`,
html: `<h1>Welcome, ${body.name}</h1><p>Your account is ready.</p>`,
});
if (error) throw new Error(error.message);
return { id: data?.id };
}, {
body: t.Object({
email: t.String({ format: "email" }),
name: t.String(),
}),
})
.listen(3000);
console.log("Server running on port 3000");src/index.ts
import { Elysia, t } from "elysia";
import { sgMail } from "./email";
new Elysia()
.post("/api/send-welcome", async ({ body }) => {
await sgMail.send({
to: body.email,
from: "noreply@yourdomain.com",
subject: `Welcome, ${body.name}`,
html: `<h1>Welcome, ${body.name}</h1><p>Your account is ready.</p>`,
});
return { sent: true };
}, {
body: t.Object({
email: t.String({ format: "email" }),
name: t.String(),
}),
})
.listen(3000);
console.log("Server running on port 3000");Going to Production
1. Verify Your Domain
Add SPF, DKIM, DMARC DNS records.
2. Use Environment Variables
# .env
SEQUENZY_API_KEY=sq_your_keyBun loads .env automatically.
3. Build for Production
bun build --target=bun src/index.ts --outdir=dist
bun run dist/index.jsBeyond Transactional
Sequenzy handles transactional sends, marketing campaigns, automated sequences, and subscriber management from one SDK. Native Stripe integration for SaaS.
Wrapping Up
- Bun.serve for lightweight HTTP servers
- Elysia for typed route handling with validation
- Auto .env loading for API key management
- npm compatibility for any email SDK
Pick your provider, copy the patterns, and start sending.