Astro กับ Resend: ส่ง Email จาก Astro
#astro13 เม.ย. 2569
Astro กับ Resend: ส่ง Email จาก Astro
Resend เป็น email API สำหรับ developers ที่ออกแบบมาให้ใช้งานง่ายและมี deliverability สูง ด้วย React Email integration และ TypeScript support ทำให้การสร้างและส่ง email จาก Astro เป็นเรื่องง่ายและสวยงาม
ทำไมต้องใช้ Resend?
- Developer-first: API ที่ออกแบบมาสำหรับ developers
- React Email: สร้าง email templates ด้วย React
- High Deliverability: อัตราการส่งสำเร็จสูง
- Analytics: ติดตาม open rates, click rates
- Free Tier: ส่งได้ 3,000 emails/เดือนฟรี
การติดตั้ง
npm install resend
npm install react @react-email/components # สำหรับ email templates
ตั้งค่า environment variables:
# .env
RESEND_API_KEY=re_your_api_key
FROM_EMAIL=noreply@yourdomain.com
สร้าง Resend Client
// src/lib/resend.ts
import { Resend } from 'resend';
export const resend = new Resend(import.meta.env.RESEND_API_KEY);
export const FROM_EMAIL = import.meta.env.FROM_EMAIL ?? 'noreply@example.com';
สร้าง Email Templates ด้วย React Email
// src/emails/WelcomeEmail.tsx
import {
Html,
Head,
Body,
Container,
Section,
Text,
Button,
Heading,
Hr,
Img,
Link,
Preview,
} from '@react-email/components';
import * as React from 'react';
interface WelcomeEmailProps {
userName: string;
confirmUrl: string;
}
export function WelcomeEmail({ userName, confirmUrl }: WelcomeEmailProps) {
return (
<Html lang="th">
<Head />
<Preview>ยินดีต้อนรับสู่ DoSystem, {userName}!</Preview>
<Body style={bodyStyle}>
<Container style={containerStyle}>
<Img
src="https://yourdomain.com/logo.png"
alt="Logo"
width={120}
height={40}
/>
<Heading style={headingStyle}>
ยินดีต้อนรับ, {userName}!
</Heading>
<Text style={textStyle}>
ขอบคุณที่สมัครสมาชิกกับเรา กรุณายืนยันอีเมลของคุณเพื่อเริ่มใช้งาน
</Text>
<Section style={buttonSectionStyle}>
<Button href={confirmUrl} style={buttonStyle}>
ยืนยันอีเมล
</Button>
</Section>
<Hr style={hrStyle} />
<Text style={footerStyle}>
หากคุณไม่ได้สมัครสมาชิก กรุณาเพิกเฉยต่ออีเมลนี้
</Text>
</Container>
</Body>
</Html>
);
}
const bodyStyle = {
backgroundColor: '#f6f9fc',
fontFamily: 'Sarabun, -apple-system, sans-serif',
};
const containerStyle = {
backgroundColor: '#ffffff',
margin: '0 auto',
padding: '20px 0 48px',
marginBottom: '64px',
maxWidth: '600px',
borderRadius: '8px',
};
const headingStyle = {
fontSize: '24px',
fontWeight: 'bold',
color: '#1a1a1a',
padding: '0 48px',
};
const textStyle = {
fontSize: '16px',
lineHeight: '26px',
color: '#404040',
padding: '0 48px',
};
const buttonSectionStyle = {
padding: '0 48px',
marginTop: '24px',
};
const buttonStyle = {
backgroundColor: '#5469d4',
borderRadius: '4px',
color: '#fff',
fontSize: '16px',
fontWeight: 'bold',
textDecoration: 'none',
textAlign: 'center' as const,
display: 'block',
padding: '12px 20px',
};
const hrStyle = {
borderColor: '#e6ebf1',
margin: '20px 0',
};
const footerStyle = {
fontSize: '12px',
color: '#8898aa',
padding: '0 48px',
};
API Route สำหรับส่ง Email
// src/pages/api/send-email.ts
import type { APIRoute } from 'astro';
import { resend, FROM_EMAIL } from '../../lib/resend';
import { WelcomeEmail } from '../../emails/WelcomeEmail';
import { render } from '@react-email/render';
import * as React from 'react';
export const POST: APIRoute = async ({ request }) => {
const { type, to, data } = await request.json();
try {
let result;
switch (type) {
case 'welcome': {
const html = render(
React.createElement(WelcomeEmail, {
userName: data.userName,
confirmUrl: data.confirmUrl,
})
);
result = await resend.emails.send({
from: FROM_EMAIL,
to,
subject: `ยินดีต้อนรับ, ${data.userName}!`,
html,
});
break;
}
case 'contact': {
result = await resend.emails.send({
from: FROM_EMAIL,
to: import.meta.env.ADMIN_EMAIL,
replyTo: to,
subject: `ข้อความจาก ${data.name}: ${data.subject}`,
text: data.message,
});
break;
}
default:
return new Response(JSON.stringify({ error: 'Unknown email type' }), {
status: 400,
});
}
return new Response(JSON.stringify({ id: result.data?.id }), {
headers: { 'Content-Type': 'application/json' },
});
} catch (error) {
console.error('Email error:', error);
return new Response(JSON.stringify({ error: 'Failed to send email' }), {
status: 500,
});
}
};
Contact Form
---
// src/pages/contact.astro
---
<html lang="th">
<body>
<h1>ติดต่อเรา</h1>
<form id="contact-form">
<input type="text" name="name" placeholder="ชื่อ" required />
<input type="email" name="email" placeholder="อีเมล" required />
<input type="text" name="subject" placeholder="หัวข้อ" required />
<textarea name="message" placeholder="ข้อความ" rows="5" required></textarea>
<button type="submit">ส่งข้อความ</button>
</form>
<script>
document.getElementById('contact-form')?.addEventListener('submit', async (e) => {
e.preventDefault();
const form = e.target as HTMLFormElement;
const formData = new FormData(form);
const response = await fetch('/api/send-email', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
type: 'contact',
to: formData.get('email'),
data: {
name: formData.get('name'),
subject: formData.get('subject'),
message: formData.get('message'),
},
}),
});
if (response.ok) {
alert('ส่งข้อความสำเร็จ!');
form.reset();
} else {
alert('เกิดข้อผิดพลาด กรุณาลองใหม่');
}
});
</script>
</body>
</html>
Batch Emails
// src/lib/emailBatch.ts
import { resend, FROM_EMAIL } from './resend';
export async function sendNewsletterBatch(
subscribers: { email: string; name: string }[],
subject: string,
html: string
) {
// Resend รองรับ batch ได้สูงสุด 100 emails ต่อ request
const batchSize = 100;
const results = [];
for (let i = 0; i < subscribers.length; i += batchSize) {
const batch = subscribers.slice(i, i + batchSize);
const emails = batch.map((sub) => ({
from: FROM_EMAIL,
to: sub.email,
subject,
html: html.replace('{{name}}', sub.name),
}));
const result = await resend.batch.send(emails);
results.push(result);
// Rate limiting
if (i + batchSize < subscribers.length) {
await new Promise((resolve) => setTimeout(resolve, 1000));
}
}
return results;
}
สรุป
Resend กับ Astro ทำให้การส่ง email เป็นเรื่องง่ายและมีประสิทธิภาพ ด้วย React Email templates ที่สวยงาม, TypeScript support, และ high deliverability ทำให้ระบบ email ของ application มีความน่าเชื่อถือสูง