Why?
Mailgun lets you measure the business impact of your email campaigns with a/b testing, using tags on your email templates for real-time analytics.
Know how your messages are rendering on different desktop clients like Outlook, Gmail, and more.
Today we’ll create sample API based on Node + Express for sending emails.
As a first step - create account on Mailgun.
https://signup.mailgun.com/new/signup
Project setup
Add dependencies as described and we’re ready to start!
{
"dependencies": {
"csv-parser": "^3.0.0",
"dotenv": "^16.0.0",
"express": "^4.17.3",
"express-validator": "6.14.0",
"form-data": "^4.0.0",
"handlebars": "4.7.7",
"jade": "^1.11.0",
"js-yaml": "4.1.0",
"mailgun.js": "^5.0.1"
}
}
Email template
Create template.hbs
file with html and add your message somewhere in that file:
<h1> { { message } } </h1>
Add your CSV file with random data:
email,name,surname
lola@gmail.com,Lola,Birkin
ivan@gmail.com,Ivan,Ivanov
petr@gmail.com,Petr,Petrov
Adjust your ENV variables in .env file:
API_KEY=XXXXXXXXXXXXXXXXXXXX
DOMAIN=XXXXXXXXXXXXXXXXXXXX
You can set bunch of configuration settings in config.yaml
file, like this:
from: example@mail.com
subject: "Attention please!"
csv_file: "emails.csv"
Building application
You can see main application file app.js
.
It’s super simple, but let’s go through main parts of this app:
- /SEND POST endpoint, with this endpoint you can send email to specific email address (json payload)
- /LIST POST endpoint, with this endpoint you can parse emails from CSV file, add them to the Maingun email list and send it (json payload)
- Port and listener
require('dotenv').config();
const express = require('express');
// PREPARE MAILGUN CLIENT
const formData = require('form-data');
const Mailgun = require('mailgun.js');
const mailgun = new Mailgun(formData);
// INITIALIZE APP
const app = express();
// REQUIRE LIBRARIES
const { body, validationResult } = require('express-validator');
const yaml = require('js-yaml');
const bodyParser = require('body-parser');
const jsonParser = bodyParser.json();
const path = require("path");
const handlebars = require("handlebars");
const fs = require("fs");
const csv = require('csv-parser');
// PREPARE MAILGUN CLIENT
const apiKey = process.env.API_KEY;
const domain = process.env.DOMAIN;
const mailgunClient = mailgun.client({ username: 'api', key: apiKey || '' });
// READ CONFIG
const fileContents = fs.readFileSync('config.yaml', 'utf8');
const CONFIG = yaml.load(fileContents);
// PREPARE TEMPLATE
const emailTemplateSource = fs.readFileSync(path.join(__dirname, "/template.hbs"), "utf8");
const template = handlebars.compile(emailTemplateSource)
const htmlToSend = template({message: "Hello"})
const fromWho = CONFIG.from;
app.post('/send', jsonParser, body('email').isEmail().withMessage('should be email'), async function (req, res) {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
const recipient = req.body.email;
const data = {
from: fromWho,
to: recipient,
subject: CONFIG.subject,
html: htmlToSend
};
try {
await mailgunClient.messages.create(domain, data);
return res.json({ status: 'ok', email: req.params.mail });
} catch (error) {
return res.json({ status: 'error', error: error });
}
});
app.post('/list', jsonParser, body('listName').isLength({ min: 2 }).withMessage('should exist and be min 2 characters length'), async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
const listName = req.body.listName;
const validListName = `${listName}@${domain}`;
let mailingList = '';
let members = [];
try {
mailingList = await mailgunClient.lists.get(validListName).catch(async (err) => {
if (err.status === 404) {
const createdMailingList = await mailgunClient.lists.create({ address: validListName });
console.info(`New mailing list ${createdMailingList.address} was created`);
return createdMailingList;
}
throw new Error(err);
});
const msgData = {
from: fromWho,
to: mailingList.address,
subject: CONFIG.subject,
html: htmlToSend
};
fs.createReadStream(CONFIG.csv_file)
.pipe(csv())
.on('data', async function(row) {
try {
members.push({
address: row.email,
name: row.name,
subscribed: true
});
const message = `New member ${row.email} was added to mailing list: ${mailingList.address}`;
console.info(message);
}
catch(err) {
console.log(err);
}
})
.on('end', async function() {
await mailgunClient.lists.members.createMembers(mailingList.address, { members: members });
try {
await mailgunClient.messages.create(domain, msgData);
} catch (error) {
return res.json({ error: error });
}
return res.json({status: 'ok'});
});
} catch (error) {
let transformedError = error;
if (error.status === 400 && error.details) {
transformedError = error.details;
}
return res.json({ error: transformedError });
}
});
const port = 3030;
app.listen(port, () => {
console.info(`server is listening on ${port}`);
});
Send one email request example
POST /send/
curl -i -H 'Accept: application/json' -d 'email=petr@gmail.com' http://localhost:3030/send
Response
{
"status": "ok"
}
Create email list and send emails request example
POST /list/
curl -i -H 'Accept: application/json' -d 'listName=myList' http://localhost:3030/list
Response
{
"status": "ok"
}
That’s it, super simple and easy. Stay tuned!