Send email from DaloyJS with Resend
Resend is a developer-first email API with first-class TypeScript types, edge-runtime support, and tight integration with React Email templates. It's an excellent default for new DaloyJS projects.
1. Provision
- Sign up at resend.com and add a sending domain under Domains. Add the SPF, DKIM, and DMARC DNS records Resend lists, then click Verify.
- Create an API key under API Keys. Use a “Sending access” key scoped to that domain.
2. Install
pnpm add resend3. Environment variables
# .env
RESEND_API_KEY=re_xxxxxxxxxxxxxxxxxxxxxx
RESEND_FROM="Acme <no-reply@acme.example.com>"4. Plugin
// src/plugins/resend.ts
import { Resend } from "resend";
import type { App } from "@daloyjs/core";
const resend = new Resend(process.env.RESEND_API_KEY);
export const resendPlugin = {
name: "resend",
register(app: App) {
app.decorate("email", {
async send({ to, subject, text, html }) {
const { data, error } = await resend.emails.send({
from: process.env.RESEND_FROM!,
to,
subject,
text,
html,
});
if (error) throw new Error(error.message);
return { id: data?.id ?? "" };
},
});
},
};
declare module "@daloyjs/core" {
interface AppState {
email: {
send(msg: {
to: string;
subject: string;
text?: string;
html?: string;
}): Promise<{ id: string }>;
};
}
}Resend SDK methods return { data, error } rather than throwing — handle the error branch and surface it through the DaloyJS error helpers.
5. Use it in a route
import { z } from "zod";
import { App, secureHeaders, rateLimit } from "@daloyjs/core";
import { resendPlugin } from "./plugins/resend";
const app = new App();
app.use(secureHeaders());
app.use(rateLimit({ windowMs: 60_000, max: 10 }));
app.register(resendPlugin);
app.route({
method: "POST",
path: "/magic-link",
operationId: "sendMagicLink",
request: {
body: z.object({ email: z.string().email() }),
},
responses: {
202: { description: "Sent", body: z.object({ id: z.string() }) },
},
handler: async ({ body, state }) => {
const link = await issueMagicLink(body.email); // your own logic
const { id } = await state.email.send({
to: body.email,
subject: "Your sign-in link",
text: `Sign in: ${link}`,
html: `<p>Sign in: <a href="${link}">${link}</a></p>`,
});
return { status: 202, body: { id } };
},
});
async function issueMagicLink(_email: string) {
return "https://acme.example.com/auth/callback?token=...";
}React Email templates
Resend reads the react field and renders it to HTML for you, so you can ship type-safe email templates as React components:
pnpm add @react-email/components react react-dom// emails/welcome.tsx
import { Html, Button, Heading, Text } from "@react-email/components";
export default function Welcome({ name }: { name: string }) {
return (
<Html>
<Heading>Welcome, {name}!</Heading>
<Text>Thanks for joining Acme.</Text>
<Button href="https://acme.example.com/start">Get started</Button>
</Html>
);
}
// in the handler
import Welcome from "../../emails/welcome";
await resend.emails.send({
from: process.env.RESEND_FROM!,
to,
subject: "Welcome to Acme",
react: Welcome({ name: "Devlin" }),
});Batch sending
Use resend.batch.send([...]) to enqueue up to 100 messages in a single API call — handy for fan-out notifications without queueing infrastructure.
Runtimes
The resend SDK uses the standard fetch API, so it runs on Node 18+, Bun, Deno, AWS Lambda, Vercel (Serverless and Edge), and Cloudflare Workers without adapters. Pair it with the edge adapters shipped by DaloyJS.
See also Postmark, SendGrid, and the email integrations overview.