Appearance
Email
Most projects need to send email.
Client / UI side
Starts off with a web form to submit the message to the the API.
Gotchas
When setting up a system for sending email, it can be tricky to know why the message didn't make it through.
If your form won't trigger the javascript actions to submit on firefox (but works on chrome) be sure to use the right button type:
If you have <button type="submit">
that triggers that axios request, change the button to <button type="button">
Captcha
Depending on your framework, there may be a widget that can help with this process.
yarn add @hcaptcha/vue-hcaptcha
yarn add @hcaptcha/vue-hcaptcha
https://github.com/hCaptcha/vue-hcaptcha
( !!! yarn add vue-hcaptcha
is very old !!! )
!!! LOCAL DEV WON'T WORK UNLESS YOU DO THIS
To make this work with local development, you need to configure your host to be something other than localhost
https://docs.hcaptcha.com/#local-development
TODO: How to register and configure sitekeys
Nodemailer
Configure Nodemailer on the API side to handle sending emails.
In a project with nodemailer already configured, searching for nodemailer
will show other places in the project that send email.
js
var nodemailer = require("nodemailer");
var express = require("express");
var winston = require("winston");
var config = require("../config");
var router = express.Router();
var logger = new winston.createLogger(config.logger.winston);
router.post("/", function (req, res) {
logger.info("contact received post");
let mailer = nodemailer.createTransport(config.transport);
logger.info("mailer transport created");
let debug = JSON.stringify(req.body);
logger.info(`received request body ${debug}`);
let text = `\n\n${req.body.fullname} has sent a message`;
text += `\n\n${req.body.link}\n\n`;
text += config.contact.footer;
console.log("contact -> message text:", text);
mailer.sendMail(
{
from: `"boilerplate-system-user" ${config.contact.email}`, // Sender address
// to: req.body.contacts.join(","), // List of receivers
to: req.body.email, // Receiver
subject: `${req.body.fullname} has sent a message`, // Subject line
text: text, // Plain text body
},
function (err, info) {
if (err) {
logger.error(err);
console.log(err);
res.json({ status: "error", err: err });
} else {
res.json({ status: "ok", info: info });
}
}
);
});
module.exports = router;
var nodemailer = require("nodemailer");
var express = require("express");
var winston = require("winston");
var config = require("../config");
var router = express.Router();
var logger = new winston.createLogger(config.logger.winston);
router.post("/", function (req, res) {
logger.info("contact received post");
let mailer = nodemailer.createTransport(config.transport);
logger.info("mailer transport created");
let debug = JSON.stringify(req.body);
logger.info(`received request body ${debug}`);
let text = `\n\n${req.body.fullname} has sent a message`;
text += `\n\n${req.body.link}\n\n`;
text += config.contact.footer;
console.log("contact -> message text:", text);
mailer.sendMail(
{
from: `"boilerplate-system-user" ${config.contact.email}`, // Sender address
// to: req.body.contacts.join(","), // List of receivers
to: req.body.email, // Receiver
subject: `${req.body.fullname} has sent a message`, // Subject line
text: text, // Plain text body
},
function (err, info) {
if (err) {
logger.error(err);
console.log(err);
res.json({ status: "error", err: err });
} else {
res.json({ status: "ok", info: info });
}
}
);
});
module.exports = router;
Sendmail
You can use a local sendmail package. Add it to your node's Dockerfile
FROM node:12
RUN apt-get update
RUN apt-get update && \
apt-get install -y \
sendmail
# And clean up the image
RUN rm -rf /var/lib/apt/lists/*
FROM node:12
RUN apt-get update
RUN apt-get update && \
apt-get install -y \
sendmail
# And clean up the image
RUN rm -rf /var/lib/apt/lists/*
Testing (Mailtrap)
It helps to use tools that you know to be reliable.
test nodemailer with mailtrap to avoid spamming
Update api configuration with something like
// mailtrap is a useful service for testing / development
exports.transport = {
host: "smtp.mailtrap.io",
port: 2525,
auth: {
user: "",
pass: "",
},
};
// mailtrap is a useful service for testing / development
exports.transport = {
host: "smtp.mailtrap.io",
port: 2525,
auth: {
user: "",
pass: "",
},
};
Get the credentials for the mailbox from mailtrap.io site.
These are not the same as the credentials to log in to the site!!!
Make sure config file (index.js
) is set to be ignored by repo in .gitignore
Testing (Cypress)
Even with mailtrap in the mix, it helps to have a test runner. This one only tackles the API. UI testing requires either disabling the captcha, or manually testing.
js
/// <reference types="cypress" />
context("Network Requests", () => {
beforeEach(() => {
console.log("API URL:", Cypress.env("API_URL"));
cy.request(Cypress.env("API_URL") + "/health")
.then((response) => {
console.log("The API health status is: ", response);
return response;
})
.its("body")
.should("be.an", "object")
.should("contain", {
status: "ok",
});
});
it("POSTS a message to the contact form", () => {
cy.request({
method: "POST",
url: Cypress.env("API_URL") + "/contact",
body: {
fullname: "Test User",
email: "boilerplate-test-user@example.com",
comment: "Testing API directly",
link: "https://gitlab.com/fern-seed/web-ui-api-db",
currentpage: "/internal/testing/path",
},
})
.then((response) => {
console.log("The POST response was: ", response);
return response;
})
.its("body")
.should("contain", {
status: "ok",
});
});
});
/// <reference types="cypress" />
context("Network Requests", () => {
beforeEach(() => {
console.log("API URL:", Cypress.env("API_URL"));
cy.request(Cypress.env("API_URL") + "/health")
.then((response) => {
console.log("The API health status is: ", response);
return response;
})
.its("body")
.should("be.an", "object")
.should("contain", {
status: "ok",
});
});
it("POSTS a message to the contact form", () => {
cy.request({
method: "POST",
url: Cypress.env("API_URL") + "/contact",
body: {
fullname: "Test User",
email: "boilerplate-test-user@example.com",
comment: "Testing API directly",
link: "https://gitlab.com/fern-seed/web-ui-api-db",
currentpage: "/internal/testing/path",
},
})
.then((response) => {
console.log("The POST response was: ", response);
return response;
})
.its("body")
.should("contain", {
status: "ok",
});
});
});