Email integrations
DaloyJS doesn't ship its own email transport. Send mail by registering a small plugin that decorates app.state with a provider client, then call it from your route handlers. The pages in this section show how to wire up the six most common transactional email providers using their official Node SDKs.
Supported providers
- AWS SES (SESv2) — pay-as-you-go SMTP/HTTP at AWS scale via
@aws-sdk/client-sesv2. Best fit when you already run on AWS or need the cheapest per-message price. - SendGrid — Twilio's established sender via
@sendgrid/mail. Good for high-volume marketing plus transactional. - Resend — modern, developer-first API via the
resendSDK. Great DX, React Email templating, edge-friendly. - Postmark — transactional-first delivery via the
postmarkSDK. Known for very high inbox placement. - Mailgun — Sinch-backed sender via
mailgun.js. Strong validation, routing, and EU/US regions. - Mailtrap — sandbox + production sending via the
mailtrapSDK. Switch between a test inbox and live sending with a single flag.
Runtime compatibility at a glance
Most provider SDKs are HTTPS-based and work on every runtime DaloyJS targets, but a few depend on Node-only APIs (filesystem, TCP, AWS Signature V4 with NodeHttpHandler) and won't run on Cloudflare Workers or Vercel Edge without adjustments.
| Provider | Node / Bun / Deno | Cloudflare Workers | Vercel Edge | AWS Lambda |
|---|---|---|---|---|
| AWS SES (SESv2) | Yes | With fetch handler & static creds | With fetch handler & static creds | Yes (IAM role) |
| SendGrid | Yes | Use Web API via fetch (SDK is Node-oriented) | Use Web API via fetch | Yes |
| Resend | Yes | Yes | Yes | Yes |
| Postmark | Yes | Call REST via fetch (SDK uses axios) | Call REST via fetch | Yes |
| Mailgun | Yes | Yes (enable useFetch: true in v12.1+) | Yes (useFetch: true) | Yes |
| Mailtrap | Yes | Call REST via fetch (SDK uses Node features) | Call REST via fetch | Yes |
Common pattern
Every guide in this section follows the same three steps: install the SDK, register a DaloyJS plugin that puts the client on app.state, then call it inside a validated route handler. The plugin shape is intentionally tiny so you can swap providers without touching business logic:
// src/plugins/email.ts
import type { App } from "@daloyjs/core";
export interface EmailMessage {
to: string;
subject: string;
text?: string;
html?: string;
}
export interface EmailSender {
send(msg: EmailMessage): Promise<{ id: string }>;
}
export function emailPlugin(sender: EmailSender) {
return {
name: "email",
register(app: App) {
app.decorate("email", sender);
},
};
}
declare module "@daloyjs/core" {
interface AppState {
email: EmailSender;
}
}Each provider page implements EmailSender with the official SDK so the rest of your app stays provider-agnostic.
Security checklist
- Keep API keys in environment variables. Never commit them. Use AWS IAM roles on Lambda and platform-managed secrets on Vercel, Cloudflare, Fly, and Render.
- Verify your sending domain. Add SPF, DKIM, and DMARC records before going live; every provider here rejects unverified senders in production.
- Validate inputs. Treat the
to,subject, and body as untrusted. Use DaloyJS validation withz.string().email()to block header injection. - Rate-limit the send route. Use the built-in rateLimit middleware (or the Redis store) on any endpoint that triggers email so abuse can't drive your bill or reputation down.
- Verify provider webhooks. If you process bounces, complaints, or opens, verify the signature on every incoming webhook before trusting its payload.