AWS SES email provider for Cloudflare Workers (full source) — and why the form working does not mean the CMS can send #2933
marks-zyz
started this conversation in
Show and tell
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
We run three client sites on EmDash + Cloudflare Workers. All of them need email that actually leaves the building: magic-link login, user invites, and form notifications. On Workers there was no provider we could use, so we wrote one for AWS SES and it has been in production since yesterday. Sharing the whole thing here because I could not find a single SES discussion in this repo, and #1770 / #1431 / #245 all circle the same gap.
Why not the existing options
cloudflareEmail()needs domain onboarding in Cloudflare Email Sending, and cloudflareEmail() provider never calls binding.send() — magic-link email silently not delivered #2253 reports it never callingbinding.send().emdash-plugin-resendandemdash-smtp, but we were already on SES for the rest of our stack and wanted one sender identity, one bounce/complaint dashboard, one DKIM setup.The one thing that cost us a day
The failure mode is silent and it is not obvious from the error. Our contact form had been delivering through SES for weeks with the same
AWS_*secrets in the Worker, so we assumed the CMS would use them too. It does not. The form endpoint was our own code calling SES directly; the CMS delivers through theemail:deliverhook, and that hook is empty until a plugin registers for it. Same credentials, same Worker, two completely separate paths. The login page just says "Email is not configured", which is accurate but does not point at the cause.Second gotcha, smaller: hooks run outside the request context, so
Astro.locals.runtime.envis not available. The env has to come fromimport("cloudflare:workers"), which works both deployed and underastro devwith the adapter.Wiring
astro.config.mjs:Secrets (
wrangler secret put), never in code:Then select it once, in Settings > Email, or over the API:
Note the hook name goes raw in the URL. Percent-encoding the colon returns "Invalid hook name format".
Proof it works, since the HTTP response is deliberately generic:
Also worth knowing: the route is
/auth/magic-link/send, not/request(that 404s quietly).The plugin
Single file, no build step, no package to install beyond
aws4fetch(~4 kB, fetch + SubtleCrypto, made for Workers). The AWS SDK does not fit: Node built-ins and a much bigger bundle.Notes from production
detail.slice(0, 500)above.retriesare not handled here:email:deliveris exclusive and a throw surfaces to the caller. Idempotency keys (Add optional idempotency keys to plugin email sends #2484) would help.Happy to turn this into a published package if there is interest, or to send it as an official
@emdash-cms/plugin-sesPR if the maintainers would rather have it in the box alongside the Cloudflare provider. Either way, the code above is MIT — take it.All reactions