Sending Emails Programmatically with Cloudflare
Intro -
In today’s digital landscape, personalized communication is key to engaging with your audience effectively. Programmatic emails offer a dynamic solution to this, allowing you to tailor content based on user behavior and preferences. In this guide, we’ll delve into the process of setting up programmatic emails using popular tools and technologies such as NPM, Node.js, Cloudflare, and Wrangler. Whether you’re a seasoned developer or just starting out, this article will provide you with the knowledge and resources needed to harness the full potential of programmatic emails and revolutionize your email marketing strategy.
In this article, we will set up a Cloudflare Worker as a private API endpoint to send emails as your personal domain.
Setup -
For the purposes of this article, we will assume your domain’s name is hiibolt.com, and the email you’d like to send from is [email protected].
Firstly, let’s create our Cloudflare worker! Ensure that you have the latest version of NodeJS and NPM installed.
npm create cloudflare\@2 email-service -y -- --wrangler-defaults
cd email-service
Next, replace the contents of src/index.tx
with the following:
import formData from 'form-data';
export default {
async fetch(request) {
if ( request.method != 'POST' ) {
return new Response("POST only!")
}
const sender = '[email protected]'
const server_key = "some_server_key"
const formData = await request.formData();
const fields = {};
for (let [key, value] of formData.entries()) {
fields[key] = value;
}
const receiver = fields["to"]
const subject = fields["subject"]
const body = fields["body"]
const send_request = new Request('https://api.mailchannels.net/tx/v1/send', {
method: 'POST',
headers: {
'content-type': 'application/json',
},
body: JSON.stringify({
personalizations: [
{
to: [{ email: receiver, name: '@hiibolt\'s Friend' }],
},
],
from: {
email: sender,
name: '@hiibolt',
},
subject,
content: [
{
type: 'text/html',
value: body,
},
],
}),
})
if ( fields["API_KEY"] == server_key ) {
const resp = await fetch(send_request)
await resp.text()
}
return new Response("ok")
},
}
Let’s explain some things. Firstly, look at the following snippet:
const formData = await request.formData();
const fields = {};
for (let [key, value] of formData.entries()) {
fields[key] = value;
}
This means our Cloudflare Worker will be expecting a Multipart Formdata-style object.
For our use-case, we want to require the following fields:
- to: The email address that we wish to send to.
- subject: The subject line of the email.
- body: The HTML body content to be sent in the email.
- API_KEY: The key to verify that you’re supposed to be using this API.
Since anyone with the link to your worker can make requests to your Worker, it’s very important that we secure it with something like an API key! We did this in the following way:
if ( fields["API_KEY"] == server_key ) {
const resp = await fetch(send_request)
await resp.text()
}
Now, run the following:
npm run deploy
This will first ask you to authenticate your Cloudflare account, but after, compiles and deploys to the Worker!
However, we aren’t done just yet! The Worker doesn’t actually have permissions to send email via your domain name. We need to create two DNS TXT records to authenticate it.
Let’s do so:
Record 1:
TXT | hiibolt.com | v=spf1 a mx include:relay.mailchannels.net ~all
Record 2:
TXT | _mailchannels | v=mc1 cfid=hiibolt.workers.dev
These records will now allow you to actually send email via the newly authenticated Cloudflare Worker!
Result -
Let’s test it out!
curl -v -F API_KEY=some_server_Key -F to=[email protected] -F subject=Hi -F body=Hello https://email-service.hiibolt.workers.dev/
Congratulations! You now have a private email API set up for free with Cloudflare Workers, through your own domain!
Outro -
Hopefully you found this article informative or helpful. If you did, follow my GitHub!
I often write these articles after struggling to find good documentation on a concept. That means there’s often a large-scale project on there related to this article, if you’d like to see it in practice! In this case, I needed an email API for my research - but didn’t want the overhead of adding a crate or to pay for Amazon SES.