
Amazon SES is a great choice if you want to send emails that reach your customers' inboxes at a low cost.
In this tutorial, you will learn how to send emails with Amazon SES in Node.js using Nodemailer. It is very easy!
ethereal.email, to test your email-sending processes. (For example, you can check if your links work correctly in your emails.)Now, let's create a folder for your project and get started. Shall we?
WARNING
This is an old-school step-by-step tutorial from which you can actually LEARN. You should follow every step to ensure you understand everything, especially if you are a beginner. If you have more experience, you might skip a few steps.
First of all, you will need to create a .env file containing your AWS Access Key ID, Secret Access Key, and the AWS region you are sending from. We will use the format shown below:
AWS_ACCESS_KEY_ID=your-access-key
AWS_SECRET_ACCESS_KEY=your-secret-key
AWS_REGION=us-east-1If you don't have an AWS account, just sign up. They offer many services in their free tier!
To send emails with AWS SES, you need to verify your email address (or your domain) beforehand. You can follow our step-by-step tutorial. It might seem like a lot, but it shouldn't take more than 20 minutes.
Finally, create a user in IAM with AmazonSESFullAccess, and generate an Access Key ID and a Secret Access Key, which you should paste into the .env file above. Later on, you should limit the permissions to the bare minimum, but while you're experimenting with SES, it might be easier to use full access.
Now that you have your credentials prepared, you can initialize your project. Navigate to your project folder and type:
npm initThis will create a package.json file that describes your project and its dependencies.
Install the following dependencies:
nodemailer is the package that we are going to use for sending our emails.@aws-sdk/client-ses the Amazon SES client for Node.jsdotenv to handle your .env filenpm install nodemailer @aws-sdk/client-ses dotenv --saveThe command above installs all the needed dependencies and saves them to your package.json.
Let's create an src folder, and an index.js file within the folder. Now, we are going to load our config values with dotenv:
import dotenv from 'dotenv'
dotenv.config({ path: '../.env' })
console.log(process.env)Run this script to make sure that your config values are loaded from your .env file.
Just enter the src folder and run the following command:
node index.jsTIP
If you want to import files like this, and do it in .js files, you have to add a "module": true, property to your package.json.
Also, we are going to use top-level await in the code, so you at least need to use Node.js 16. We used Node.js 22 while writing this tutorial.
Now, you can import the SESClient, SendRawEmailCommand and the default credential provider. The last will automatically use the AWS_ACCESS_KEY_ID and the AWS_SECRET_ACCESS_KEY environment variables defined in your .env file. Nodemailer will actually use the SendRawEmailCommand in the background. We just need the most basic email-related SES command!
import { SESClient, SendRawEmailCommand } from '@aws-sdk/client-ses'
import { defaultProvider } from '@aws-sdk/credential-provider-node'You can actually create the SES client:
const sesClient = new SESClient({
region: process.env.AWS_REGION,
credentials: defaultProvider()
})As stated at the beginning of the tutorial, we won't directly use the SES client. It will be the transport layer of Nodemailer. This way, if you ever decide to use a different provider, it will be very easy to switch.
Just import nodemailer:
import nodemailer from 'nodemailer'And create your transport layer:
const transportLayer = nodemailer.createTransport({
SES: {
ses: sesClient
aws: { SendRawEmailCommand }
}
})As you can see, we pass down the sesClient, and the SendRawEmailCommand. This is how Nodemailer will exactly know what method to use with SES.
Now, it's time for sending! Just use the .sendMail function of Nodemailer. Pass down the following arguments:
const response = await transportLayer.sendMail({
from: 'you@yourdomain.com',
to: 'someoneelse@someotherdomain.com',
subject: 'Testing Sending from SES',
text: 'This is a test email sent with Nodemailer and SES'
})
console.log(response)That's it! Your email is sent! If you take a look at the console output, you will notice that the response contains a messageId. If you build an application that sends out emails, this message id can be extremely useful, especially if you are planning to handle bounces and complaints.
If you followed the tutorial to this point, your project should look something like the following. Also, you can check out our version of the code on Github
Your folder structure should look like this:
/your-project/
|
| /src/
| |
| | index.js
|
| .env
| package.jsonYour .env file should look like this:
AWS_ACCESS_KEY_ID=your-access-key
AWS_SECRET_ACCESS_KEY=your-secret-key
AWS_REGION=us-east-1Your package.json should look like this:
{
"name": "how-to-send-an-email-in-nodejs-using-amazon-ses-with-nodemailer",
"version": "0.0.1",
"type": "module",
"description": "Code example for the article titled \"How to Send an Email in Node.js Using Amazon SES With Nodemailer\"",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/bluefox-email/how-to-send-an-email-in-nodejs-using-amazon-ses-with-nodemailer.git"
},
"author": "Innovaris Group LLC.",
"license": "MIT",
"bugs": {
"url": "https://github.com/bluefox-email/how-to-send-an-email-in-nodejs-using-amazon-ses-with-nodemailer/issues"
},
"homepage": "https://github.com/bluefox-email/how-to-send-an-email-in-nodejs-using-amazon-ses-with-nodemailer#readme",
"dependencies": {
"@aws-sdk/client-ses": "^3.750.0",
"dotenv": "^16.4.7",
"nodemailer": "^6.10.0"
}
}Your src/index.js should look like this:
import dotenv from 'dotenv'
dotenv.config({ path: '../.env' })
import nodemailer from 'nodemailer'
import { SESClient, SendRawEmailCommand } from '@aws-sdk/client-ses'
import { defaultProvider } from '@aws-sdk/credential-provider-node'
const sesClient = new SESClient({
region: process.env.AWS_REGION,
credentials: defaultProvider()
})
const transportLayer = nodemailer.createTransport({
SES: {
ses: sesClient,
aws: { SendRawEmailCommand }
}
})
const response = await transportLayer.sendMail({
from: 'you@yourdomain.com',
to: 'someoneelse@someotherdomain.com',
subject: 'Testing Sending from SES',
text: 'This is a test email sent with Nodemailer and SES'
})
console.log(response)If you set up everything correctly, you should have received an email after running the script.
If you want to send out emails programmatically, I assume you want to send transactional emails or any other email that you send from your backend to your customers. In that case, you most likely want to send HTML emails.
Here is how you can do it:
const response = await transportLayer.sendMail({
from: 'you@yourdomain.com',
to: 'someoneelse@someotherdomain.com',
subject: 'Testing Sending from SES - HTML',
text: 'This is a test email sent with Nodemailer and SES',
html: '<h1>This is a test email sent with Nodemailer and SES</h1><img src="https://bluefox.email/assets/bluefoxemail-logo.png" width="200" alt="bluefox.email logo"/>'
})Although you send an HTML email, you might have noticed that we also sent the text version of the email. It is advised to do so.
You should see something like this in your inbox: 
HTML emails are tough
Many people are surprised how hard it is to code HTML emails. You need to support tons of email clients on tons of devices, all of them with their own quirks... (I'm looking at you, Outlook!)
If you don't want to suffer with email HTML coding, and you still want to use AWS SES for sending out emails, then you might LOVE bluefox.email. You can use a nice drag 'n' drop editor to create your emails, and you can maintain consistent-looking emails across your transactional and marketing emails.
On top of that, you get friendly analytics, an intuitive way to automate your email flows, and we handle bounces and complaints for you.
If that's interesting for you, we would love you to give it a try.
In many cases, you might want to send attachments with your emails. For example, you may need to send an invoice in PDF format or attach a calendar invite to your email.
Here is how you can do that:
const response = await transportLayer.sendMail({
from: 'you@yourdomain.com',
to: 'someoneelse@someotherdomain.com',
subject: 'Testing Sending from SES - HTML with attachments',
text: 'This is a test email sent with Nodemailer and SES',
html: '<h1>This is a test email sent with Nodemailer and SES</h1>',
attachments: [
{
'filename': 'example.txt',
'content': 'Ymx1ZWZveC5lbWFpbCBhdHRhY2htZW50IGV4YW1wbGUh',
'encoding': 'base64'
}
]
})Note that we set the encoding to base64. In the example above, it's not necessary because the attachment is a simple text file. However, when sending PDFs or other binary files, encoding them in base64 is a recommended approach. While you could reference a file from your file system, it's often better to handle everything in memory for flexibility and performance.
Send attachments with bluefox.email
As you might expect, bluefox.email also supports attachments. We use the very same interface, allowing you to manage all of your emails from a single platform: bluefox.email. You get 3,000 credits per month for a whole year when you register an account.
Whether you send transactional or marketing emails, subject lines, pre-header texts, and the sender name are extremely important. When used effectively, these elements help your emails stand out in your customers' inboxes, increasing the open rate.
Additionally, you must provide a way for users to unsubscribe from your emails (except for transactional emails, which are essential for fulfilling a functionality). You should also implement double opt-in to confirm new subscribers.
If you want to get production access to Amazon SES, you should also implement "One-click unsubscribe" (bluefox.email does this automatically for you) and handle bounces and complaints.
I know this may seem overwhelming, but it's crucial to take all necessary steps to be a responsible sender. No one likes spammers—send responsibly!