-
Notifications
You must be signed in to change notification settings - Fork 10.2k
Email Workers local development #21399
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 7 commits
Commits
Show all changes
23 commits
Select commit
Hold shift + click to select a range
e0b5df5
Email Workers local development
celso 40d3dd8
Update src/content/docs/email-routing/email-workers/local-development…
celso ce3c1a2
Update src/content/docs/email-routing/email-workers/local-development…
celso 2347699
Update src/content/docs/email-routing/email-workers/local-development…
celso 160128a
Update src/content/docs/email-routing/email-workers/local-development…
celso f3184c6
Update src/content/docs/email-routing/email-workers/local-development…
celso 1235570
preview link no longer required
celso 8441c2d
changelog entry
celso 114241b
changes required
celso 9a154dd
typo
celso 966a3ba
closes code section
celso fbc3ab6
Update src/content/docs/email-routing/email-workers/local-development…
celso 154ebf1
Update src/content/docs/email-routing/email-workers/local-development…
celso 7409620
Update src/content/docs/email-routing/email-workers/local-development…
celso a238063
Update src/content/docs/email-routing/email-workers/local-development…
celso c0f84a4
Update src/content/docs/email-routing/email-workers/local-development…
celso 3538283
Update src/content/docs/email-routing/email-workers/local-development…
celso 75de507
Update src/content/docs/email-routing/email-workers/local-development…
celso 32662f3
Update src/content/docs/email-routing/email-workers/local-development…
celso eb91077
Update src/content/docs/email-routing/email-workers/local-development…
celso 14621f6
Update src/content/docs/email-routing/email-workers/local-development…
celso c3bc5cc
update changelog
celso 3fb0a90
Merge branch 'celso/email-localdev' of github.com:cloudflare/cloudfla…
celso File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
205 changes: 205 additions & 0 deletions
205
src/content/docs/email-routing/email-workers/local-development.mdx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,205 @@ | ||||||
| --- | ||||||
| title: Local Development | ||||||
| sidebar: | ||||||
| order: 6 | ||||||
| badge: Beta | ||||||
|
|
||||||
| --- | ||||||
|
|
||||||
| import { Render, Type, MetaInfo, WranglerConfig } from "~/components"; | ||||||
|
|
||||||
| You can test the behavior of an Email Worker script in local development using [wrangler dev](/workers/wrangler/commands/#dev). | ||||||
|
|
||||||
| This is the minimal wrangler configuration required to run an Email Worker locally: | ||||||
|
|
||||||
| <WranglerConfig> | ||||||
|
|
||||||
| ```jsonc | ||||||
| { | ||||||
| "send_email": [ | ||||||
| { | ||||||
| "name": "EMAIL" | ||||||
| } | ||||||
| ] | ||||||
| } | ||||||
| ``` | ||||||
|
|
||||||
| </WranglerConfig> | ||||||
|
|
||||||
| :::note | ||||||
|
|
||||||
| If you want to deploy your script you need to enable Email Routing [enabled](/email-routing/get-started/enable-email-routing/) and have at least one verified [destination address](/email-routing/setup/email-routing-addresses/#destination-addresses). | ||||||
|
|
||||||
| ::: | ||||||
|
|
||||||
| You can now test receiving, replying and sending emails in your local environment. | ||||||
|
|
||||||
| ## Receiving an email | ||||||
celso marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
|
|
||||||
| Consider this example Email Worker script that uses the opensource [postal-mime](https://www.npmjs.com/package/postal-mime) email parser: | ||||||
celso marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
|
|
||||||
| ```ts | ||||||
| import * as PostalMime from 'postal-mime'; | ||||||
|
|
||||||
| export default { | ||||||
| async email(message, env, ctx) { | ||||||
| const parser = new PostalMime.default(); | ||||||
| const rawEmail = new Response(message.raw); | ||||||
| const email = await parser.parse(await rawEmail.arrayBuffer()); | ||||||
| console.log(email); | ||||||
| }, | ||||||
| }; | ||||||
|
|
||||||
| Now when you run `npx wrangler dev`, wrangler will expose a local `/cdn-cgi/handler/email` endpoint that you can `POST` email messages to and trigger your Worker's `email()` handler: | ||||||
|
|
||||||
| ```bash | ||||||
| curl -X POST 'http://localhost:8787/cdn-cgi/handler/email' \ | ||||||
celso marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
| --url-query '[email protected]' \ | ||||||
| --url-query '[email protected]' \ | ||||||
| --header 'Content-Type: application/json' \ | ||||||
| --data-raw 'Received: from smtp.example.com (127.0.0.1) | ||||||
| by cloudflare-email.com (unknown) id 4fwwffRXOpyR | ||||||
| for <[email protected]>; Tue, 27 Aug 2024 15:50:20 +0000 | ||||||
| From: "John" <[email protected]> | ||||||
| Reply-To: [email protected] | ||||||
| To: [email protected] | ||||||
| Subject: Testing Email Workers Local Dev | ||||||
| Content-Type: text/html; charset="windows-1252" | ||||||
| X-Mailer: Curl | ||||||
| Date: Tue, 27 Aug 2024 08:49:44 -0700 | ||||||
| Message-ID: <6114391943504294873000@ZSH-GHOSTTY> | ||||||
|
|
||||||
| Hi there' | ||||||
| ``` | ||||||
|
|
||||||
| This is what you get in the console: | ||||||
|
|
||||||
| ```json | ||||||
| { | ||||||
| headers: [ | ||||||
| { | ||||||
| key: 'received', | ||||||
| value: 'from smtp.example.com (127.0.0.1) by cloudflare-email.com (unknown) id 4fwwffRXOpyR for <[email protected]>; Tue, 27 Aug 2024 15:50:20 +0000' | ||||||
| }, | ||||||
| { key: 'from', value: '"John" <[email protected]>' }, | ||||||
| { key: 'reply-to', value: '[email protected]' }, | ||||||
| { key: 'to', value: '[email protected]' }, | ||||||
| { key: 'subject', value: 'Testing Email Workers Local Dev' }, | ||||||
| { key: 'content-type', value: 'text/html; charset="windows-1252"' }, | ||||||
| { key: 'x-mailer', value: 'Curl' }, | ||||||
| { key: 'date', value: 'Tue, 27 Aug 2024 08:49:44 -0700' }, | ||||||
| { | ||||||
| key: 'message-id', | ||||||
| value: '<6114391943504294873000@ZSH-GHOSTTY>' | ||||||
| } | ||||||
| ], | ||||||
| from: { address: '[email protected]', name: 'John' }, | ||||||
| to: [ { address: '[email protected]', name: '' } ], | ||||||
| replyTo: [ { address: '[email protected]', name: '' } ], | ||||||
| subject: 'Testing Email Workers Local Dev', | ||||||
| messageId: '<6114391943504294873000@ZSH-GHOSTTY>', | ||||||
| date: '2024-08-27T15:49:44.000Z', | ||||||
| html: 'Hi there\n', | ||||||
| attachments: [] | ||||||
| } | ||||||
| ``` | ||||||
|
|
||||||
| ## Sending an email | ||||||
celso marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
|
|
||||||
| Wrangler can also simulate sending emails locally. Consider this example Email Worker script that uses the [mimetext](https://www.npmjs.com/package/mimetext) npm package: | ||||||
celso marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
|
|
||||||
| ```ts | ||||||
| import { EmailMessage } from "cloudflare:email"; | ||||||
| import { createMimeMessage } from 'mimetext'; | ||||||
|
|
||||||
| export default { | ||||||
| async fetch(request, env, ctx) { | ||||||
| const msg = createMimeMessage(); | ||||||
| msg.setSender({ name: 'Sending email test', addr: '[email protected]' }); | ||||||
| msg.setRecipient('[email protected]'); | ||||||
| msg.setSubject('An email generated in a worker'); | ||||||
| msg.addMessage({ | ||||||
| contentType: 'text/plain', | ||||||
| data: `Congratulations, you just sent an email from a worker.`, | ||||||
| }); | ||||||
|
|
||||||
| var message = new EmailMessage('[email protected]', '[email protected]', msg.asRaw()); | ||||||
| await env.EMAIL.send(message); | ||||||
| return Response.json({ ok: true }); | ||||||
| } | ||||||
| }; | ||||||
| ``` | ||||||
|
|
||||||
| Now when you run `npx wrangler dev`, go to http://localhost:8787/ to trigger the `fetch()` handler and send the email. You will see the follow message in your terminal: | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Let's avoid "see" here. |
||||||
|
|
||||||
| ```bash | ||||||
celso marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
| ⎔ Starting local server... | ||||||
| [wrangler:inf] Ready on http://localhost:8787 | ||||||
| [wrangler:inf] GET / 200 OK (19ms) | ||||||
| [wrangler:inf] send_email binding called with the following message: | ||||||
| /var/folders/33/pn86qymd0w50htvsjp93rys40000gn/T/miniflare-f9be031ff417b2e67f2ac4cf94cb1b40/files/email/33e0a255-a7df-4f40-b712-0291806ed2b3.eml | ||||||
| ``` | ||||||
|
|
||||||
| Wrangler simulated `env.EMAIL.send()` by writing the email to a local file in [eml](https://datatracker.ietf.org/doc/html/rfc5322) format. The file contains the raw email message: | ||||||
|
|
||||||
| ``` | ||||||
| Date: Fri, 04 Apr 2025 12:27:08 +0000 | ||||||
| From: =?utf-8?B?U2VuZGluZyBlbWFpbCB0ZXN0?= <[email protected]> | ||||||
| To: <[email protected]> | ||||||
| Message-ID: <[email protected]> | ||||||
| Subject: =?utf-8?B?QW4gZW1haWwgZ2VuZXJhdGVkIGluIGEgd29ya2Vy?= | ||||||
| MIME-Version: 1.0 | ||||||
| Content-Type: text/plain; charset=UTF-8 | ||||||
| Content-Transfer-Encoding: 7bit | ||||||
|
|
||||||
| Congratulations, you just sent an email from a worker. | ||||||
| ``` | ||||||
|
|
||||||
| ## Replying to and forwarding messages | ||||||
celso marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
|
|
||||||
| Likewise, [`EmailMessage`](/email-routing/email-workers/runtime-api/#emailmessage-definition)'s `forward()` and `reply()` methods are also simulated locally. Consider this Worker that receives an email, parses it, replies to the sender, and forwards the original message to one your verified recipient addresses: | ||||||
|
|
||||||
| ```ts | ||||||
| import * as PostalMime from 'postal-mime'; | ||||||
| import { createMimeMessage } from 'mimetext'; | ||||||
| import { EmailMessage } from 'cloudflare:email'; | ||||||
|
|
||||||
| export default { | ||||||
| async email(message, env: any, ctx: any) { | ||||||
| // parses incoming message | ||||||
| const parser = new PostalMime.default(); | ||||||
| const rawEmail = new Response(message.raw); | ||||||
| const email = await parser.parse(await rawEmail.arrayBuffer()); | ||||||
|
|
||||||
| // creates some ticket | ||||||
| // const ticket = await createTicket(email); | ||||||
|
|
||||||
| // creates reply message | ||||||
| const msg = createMimeMessage(); | ||||||
| msg.setSender({ name: 'Thank you for your contact', addr: '[email protected]' }); | ||||||
| msg.setRecipient(message.from); | ||||||
| msg.setHeader('In-Reply-To', message.headers.get('Message-ID')); | ||||||
| msg.setSubject('An email generated in a worker'); | ||||||
| msg.addMessage({ | ||||||
| contentType: 'text/plain', | ||||||
| data: `This is an automated reply. We received you email with the subject "${email.subject}", and will handle it as soon as possible.`, | ||||||
| }); | ||||||
|
|
||||||
| const replyMessage = new EmailMessage('[email protected]', message.from, msg.asRaw()); | ||||||
|
|
||||||
| await message.reply(replyMessage); | ||||||
| await message.forward("[email protected]"); | ||||||
| }, | ||||||
| }; | ||||||
| ``` | ||||||
|
|
||||||
| Run `npx wrangler dev` and use curl to POST the same message from the [Receiving an email](#receiving-an-email) example. Your terminal will show you where to find the replied message in your local disk and to whom the email was forwarded: | ||||||
celso marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
|
|
||||||
| ```bash | ||||||
celso marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
| ⎔ Starting local server... | ||||||
| [wrangler:inf] Ready on http://localhost:8787 | ||||||
| [wrangler:inf] Email handler replied to sender with the following message: | ||||||
| /var/folders/33/pn86qymd0w50htvsjp93rys40000gn/T/miniflare-381a79d7efa4e991607b30a079f6b17d/files/email/a1db7ebb-ccb4-45ef-b315-df49c6d820c0.eml | ||||||
| [wrangler:inf] Email handler forwarded message with | ||||||
| rcptTo: [email protected] | ||||||
| ``` | ||||||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.