Email System
DBS sends transactional emails via the Resend API. All email-sending logic is centralized in src/lib/email.ts.
Configuration
Email settings can be stored two ways:
- Environment variables —
RESEND_API_KEY,EMAIL_FROMin.env - Database settings — via
getSystemConfig(), which falls back to.envif the DB value isn’t set yet
// src/lib/email.ts
import { Resend } from 'resend';
import { getSystemConfig } from './config';
import { EmailTemplate } from '@/components/email-template';
export async function sendVerificationEmail({
to,
name,
verificationUrl,
}: {
to: string;
name: string;
verificationUrl: string;
}) {
const apiKey = await getSystemConfig('RESEND_API_KEY', process.env.RESEND_API_KEY);
const from = await getSystemConfig('EMAIL_FROM', process.env.EMAIL_FROM);
const resend = new Resend(apiKey);
const { error } = await resend.emails.send({
from: from ?? 'noreply@example.com',
to,
subject: 'Verify your email address',
react: EmailTemplate({
type: 'verification',
actionUrl: verificationUrl,
userName: name,
}),
});
if (error) {
throw new Error(`Failed to send verification email: ${error.message}`);
}
}Sending Emails
Import and call the appropriate function:
import { sendVerificationEmail } from '@/lib/email';
// Triggered by Better Auth after registration
await sendVerificationEmail({
to: user.email,
name: user.name,
verificationUrl: `${process.env.NEXT_PUBLIC_APP_URL}/api/auth/verify-email?token=${token}`,
});getSystemConfig Pattern
getSystemConfig is the recommended way to read any configuration that might be stored in the database or environment:
import { getSystemConfig } from '@/lib/config';
// Read from DB settings first, fall back to .env
const apiKey = await getSystemConfig('RESEND_API_KEY', process.env.RESEND_API_KEY);
const fromEmail = await getSystemConfig('EMAIL_FROM', process.env.EMAIL_FROM);
const appName = await getSystemConfig('APP_NAME', 'DBS Dashboard');This allows admins to update configuration keys through the Settings UI without redeploying.
Email Template
The React Email template (src/components/email-template.tsx) supports multiple email types:
// src/components/email-template.tsx
import { Html, Body, Container, Heading, Button, Text } from '@react-email/components';
interface EmailTemplateProps {
type: 'verification' | 'reset-password' | 'invite' | 'notification';
actionUrl: string;
userName?: string;
message?: string;
}
export function EmailTemplate({ type, actionUrl, userName, message }: EmailTemplateProps) {
return (
<Html>
<Body style={{ fontFamily: 'sans-serif', backgroundColor: '#f4f4f4' }}>
<Container style={{ background: '#fff', padding: '24px', borderRadius: '8px' }}>
<Heading>DBS — {typeLabels[type]}</Heading>
{userName && <Text>Hi {userName},</Text>}
<Text>{message ?? typeMessages[type]}</Text>
<Button href={actionUrl} style={{ background: '#000', color: '#fff' }}>
{typeButtonLabels[type]}
</Button>
<Text style={{ color: '#888', fontSize: '12px' }}>
If you did not request this, you can safely ignore this email.
</Text>
</Container>
</Body>
</Html>
);
}Test Email Endpoint
DBS includes a test endpoint to verify your Resend configuration:
POST /api/mail
Content-Type: application/json
{ "to": "test@example.com" }The POST /api/mail endpoint requires settings.manage permission. Never expose it publicly or use it in automated tests against a real Resend account.
Adding New Email Types
Add the type to EmailTemplate
type: 'verification' | 'reset-password' | 'invite' | 'my-new-type'Add label, message, and button text
const typeLabels = {
// ...
'my-new-type': 'Your Custom Subject',
};Create a sender function in email.ts
export async function sendCustomEmail({ to, name, actionUrl }) {
const apiKey = await getSystemConfig('RESEND_API_KEY', process.env.RESEND_API_KEY);
const resend = new Resend(apiKey);
await resend.emails.send({
from: process.env.EMAIL_FROM!,
to,
subject: 'Your Custom Email',
react: EmailTemplate({ type: 'my-new-type', actionUrl, userName: name }),
});
}Call it from your API route
await sendCustomEmail({ to: user.email, name: user.name, actionUrl });Last updated on