# Common Patterns ## 1. Allowlist/Blocklist ```typescript // Allowlist const allowed = ["user@example.com", "trusted@corp.com"]; if (!allowed.includes(message.from)) { message.setReject("Not allowed"); return; } await message.forward("inbox@corp.com"); ``` ## 2. Parse Email Body ```typescript import PostalMime from 'postal-mime'; export default { async email(message, env, ctx) { // CRITICAL: Consume stream immediately const raw = await message.raw.arrayBuffer(); const parser = new PostalMime(); const email = await parser.parse(raw); console.log({ subject: email.subject, text: email.text, html: email.html, from: email.from.address, attachments: email.attachments.length }); await message.forward("inbox@corp.com"); } } satisfies ExportedHandler; ``` ## 3. Spam Filter ```typescript const score = parseFloat(message.headers.get("x-cf-spamh-score") || "0"); if (score > 5) { message.setReject("Spam detected"); return; } await message.forward("inbox@corp.com"); ``` ## 4. Archive to R2 ```typescript interface Env { R2: R2Bucket; } export default { async email(message, env, ctx) { const raw = await message.raw.arrayBuffer(); const key = `${new Date().toISOString()}-${message.from}.eml`; await env.R2.put(key, raw, { httpMetadata: { contentType: "message/rfc822" } }); await message.forward("inbox@corp.com"); } } satisfies ExportedHandler; ``` ## 5. Store Metadata in KV ```typescript import PostalMime from 'postal-mime'; interface Env { KV: KVNamespace; } export default { async email(message, env, ctx) { const raw = await message.raw.arrayBuffer(); const parser = new PostalMime(); const email = await parser.parse(raw); const metadata = { from: email.from.address, subject: email.subject, timestamp: new Date().toISOString(), size: raw.byteLength }; await env.KV.put(`email:${Date.now()}`, JSON.stringify(metadata)); await message.forward("inbox@corp.com"); } } satisfies ExportedHandler; ``` ## 6. Subject-Based Routing ```typescript export default { async email(message, env, ctx) { const subject = message.headers.get("subject")?.toLowerCase() || ""; if (subject.includes("[urgent]")) { await message.forward("oncall@corp.com"); } else if (subject.includes("[billing]")) { await message.forward("billing@corp.com"); } else if (subject.includes("[support]")) { await message.forward("support@corp.com"); } else { await message.forward("general@corp.com"); } } } satisfies ExportedHandler; ``` ## 7. Auto-Reply ```typescript interface Env { EMAIL: SendEmail; REPLIED: KVNamespace; } export default { async email(message, env, ctx) { const msgId = message.headers.get("message-id"); if (msgId && await env.REPLIED.get(msgId)) { await message.forward("archive@corp.com"); return; } ctx.waitUntil((async () => { await env.EMAIL.send({ from: "noreply@yourdomain.com", to: message.from, subject: "Re: " + (message.headers.get("subject") || ""), text: "Thank you. We'll respond within 24h." }); if (msgId) await env.REPLIED.put(msgId, "1", { expirationTtl: 604800 }); })()); await message.forward("support@corp.com"); } } satisfies ExportedHandler; ``` ## 8. Extract Attachments ```typescript import PostalMime from 'postal-mime'; interface Env { ATTACHMENTS: R2Bucket; } export default { async email(message, env, ctx) { const parser = new PostalMime(); const email = await parser.parse(await message.raw.arrayBuffer()); for (const att of email.attachments) { const key = `${Date.now()}-${att.filename}`; await env.ATTACHMENTS.put(key, att.content, { httpMetadata: { contentType: att.mimeType } }); } await message.forward("inbox@corp.com"); } } satisfies ExportedHandler; ``` ## 9. Log to D1 ```typescript import PostalMime from 'postal-mime'; interface Env { DB: D1Database; } export default { async email(message, env, ctx) { const parser = new PostalMime(); const email = await parser.parse(await message.raw.arrayBuffer()); ctx.waitUntil( env.DB.prepare("INSERT INTO log (ts, from_addr, subj) VALUES (?, ?, ?)") .bind(new Date().toISOString(), email.from.address, email.subject || "") .run() ); await message.forward("inbox@corp.com"); } } satisfies ExportedHandler; ``` ## 10. Multi-Tenant ```typescript interface Env { TENANTS: KVNamespace; } export default { async email(message, env, ctx) { const subdomain = message.to.split("@")[1].split(".")[0]; const config = await env.TENANTS.get(subdomain, "json") as { forward: string } | null; if (!config) { message.setReject("Unknown tenant"); return; } await message.forward(config.forward); } } satisfies ExportedHandler; ``` ## Summary | Pattern | Use Case | Storage | |---------|----------|---------| | Allowlist | Security | None | | Parse | Body/attachments | None | | Spam Filter | Reduce spam | None | | R2 Archive | Email storage | R2 | | KV Meta | Analytics | KV | | Subject Route | Dept routing | None | | Auto-Reply | Support | KV | | Attachments | Doc mgmt | R2 | | D1 Log | Audit trail | D1 | | Multi-Tenant | SaaS | KV |