Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Comprehensive Cloudflare platform skill covering Workers, D1, R2, KV, AI, Durable Objects, and security.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
references/email-routing/patterns.md
1# Common Patterns23## 1. Allowlist/Blocklist45```typescript6// Allowlist7const allowed = ["[email protected]", "[email protected]"];8if (!allowed.includes(message.from)) {9message.setReject("Not allowed");10return;11}12await message.forward("[email protected]");13```1415## 2. Parse Email Body1617```typescript18import PostalMime from 'postal-mime';1920export default {21async email(message, env, ctx) {22// CRITICAL: Consume stream immediately23const raw = await message.raw.arrayBuffer();2425const parser = new PostalMime();26const email = await parser.parse(raw);2728console.log({29subject: email.subject,30text: email.text,31html: email.html,32from: email.from.address,33attachments: email.attachments.length34});3536await message.forward("[email protected]");37}38} satisfies ExportedHandler;39```4041## 3. Spam Filter4243```typescript44const score = parseFloat(message.headers.get("x-cf-spamh-score") || "0");45if (score > 5) {46message.setReject("Spam detected");47return;48}49await message.forward("[email protected]");50```5152## 4. Archive to R25354```typescript55interface Env { R2: R2Bucket; }5657export default {58async email(message, env, ctx) {59const raw = await message.raw.arrayBuffer();6061const key = `${new Date().toISOString()}-${message.from}.eml`;62await env.R2.put(key, raw, {63httpMetadata: { contentType: "message/rfc822" }64});6566await message.forward("[email protected]");67}68} satisfies ExportedHandler<Env>;69```7071## 5. Store Metadata in KV7273```typescript74import PostalMime from 'postal-mime';7576interface Env { KV: KVNamespace; }7778export default {79async email(message, env, ctx) {80const raw = await message.raw.arrayBuffer();81const parser = new PostalMime();82const email = await parser.parse(raw);8384const metadata = {85from: email.from.address,86subject: email.subject,87timestamp: new Date().toISOString(),88size: raw.byteLength89};9091await env.KV.put(`email:${Date.now()}`, JSON.stringify(metadata));92await message.forward("[email protected]");93}94} satisfies ExportedHandler<Env>;95```9697## 6. Subject-Based Routing9899```typescript100export default {101async email(message, env, ctx) {102const subject = message.headers.get("subject")?.toLowerCase() || "";103104if (subject.includes("[urgent]")) {105await message.forward("[email protected]");106} else if (subject.includes("[billing]")) {107await message.forward("[email protected]");108} else if (subject.includes("[support]")) {109await message.forward("[email protected]");110} else {111await message.forward("[email protected]");112}113}114} satisfies ExportedHandler;115```116117## 7. Auto-Reply118119```typescript120interface Env {121EMAIL: SendEmail;122REPLIED: KVNamespace;123}124125export default {126async email(message, env, ctx) {127const msgId = message.headers.get("message-id");128129if (msgId && await env.REPLIED.get(msgId)) {130await message.forward("[email protected]");131return;132}133134ctx.waitUntil((async () => {135await env.EMAIL.send({136from: "[email protected]",137to: message.from,138subject: "Re: " + (message.headers.get("subject") || ""),139text: "Thank you. We'll respond within 24h."140});141if (msgId) await env.REPLIED.put(msgId, "1", { expirationTtl: 604800 });142})());143144await message.forward("[email protected]");145}146} satisfies ExportedHandler<Env>;147```148149## 8. Extract Attachments150151```typescript152import PostalMime from 'postal-mime';153154interface Env { ATTACHMENTS: R2Bucket; }155156export default {157async email(message, env, ctx) {158const parser = new PostalMime();159const email = await parser.parse(await message.raw.arrayBuffer());160161for (const att of email.attachments) {162const key = `${Date.now()}-${att.filename}`;163await env.ATTACHMENTS.put(key, att.content, {164httpMetadata: { contentType: att.mimeType }165});166}167168await message.forward("[email protected]");169}170} satisfies ExportedHandler<Env>;171```172173## 9. Log to D1174175```typescript176import PostalMime from 'postal-mime';177178interface Env { DB: D1Database; }179180export default {181async email(message, env, ctx) {182const parser = new PostalMime();183const email = await parser.parse(await message.raw.arrayBuffer());184185ctx.waitUntil(186env.DB.prepare("INSERT INTO log (ts, from_addr, subj) VALUES (?, ?, ?)")187.bind(new Date().toISOString(), email.from.address, email.subject || "")188.run()189);190191await message.forward("[email protected]");192}193} satisfies ExportedHandler<Env>;194```195196## 10. Multi-Tenant197198```typescript199interface Env { TENANTS: KVNamespace; }200201export default {202async email(message, env, ctx) {203const subdomain = message.to.split("@")[1].split(".")[0];204const config = await env.TENANTS.get(subdomain, "json") as { forward: string } | null;205206if (!config) {207message.setReject("Unknown tenant");208return;209}210211await message.forward(config.forward);212}213} satisfies ExportedHandler<Env>;214```215216## Summary217218| Pattern | Use Case | Storage |219|---------|----------|---------|220| Allowlist | Security | None |221| Parse | Body/attachments | None |222| Spam Filter | Reduce spam | None |223| R2 Archive | Email storage | R2 |224| KV Meta | Analytics | KV |225| Subject Route | Dept routing | None |226| Auto-Reply | Support | KV |227| Attachments | Doc mgmt | R2 |228| D1 Log | Audit trail | D1 |229| Multi-Tenant | SaaS | KV |230