Servers use the SMTP protocol for sending and receiving email. SMTP is an application layer protocol, like HTTP, FTP, and SSH.
To start receiving email, one needs to have an SMTP server listening on port 25. An SMTP server is a computer/machine/process/application that accepts TCP connections and uses the SMTP protocol. Similarly, the HTTP client normally connects to the host's TCP port 80.
The smtp-server npm module is a library for creating SMTP servers in Node.js similar to how you create HTTP servers.
The cool thing is that this way it's very easy to see everything that is sent your way by accepting all incoming mail. Most full-blown email server applications like Postfix by default restrict recipient addresses and reject other emails. With your own email server, you can check emails imperatively. Similar to how Apache Server and nginx are configurable using httpd.conf and nginx.conf, but with Express.js you can achieve anything you want much easier.
const SMTPServer = require('smtp-server').SMTPServer; const server = new SMTPServer({ onData: (stream, session, callback) => { // Process message } }); server.listen(25);
An incoming email fires the onData
callback. Use this handler to get the stream for the incoming message:
const SMTPServer = require('smtp-server').SMTPServer; const server = new SMTPServer({ onData: (stream, session, callback) => { stream.pipe(process.stdout); // Print the message to stdout stream.on('end', callback); // Must run the callback once done } }); server.listen(25);
The session
object passed to the handler includes useful properties such as remoteAddress
(the IP address of the connected client) and envelope
. The envelope is the email message metadata sent outside of the message headers and body and includes the mailFrom
and rcptTo
properties.
The cool thing is that the stream includes the message as is with no modifications.
If your server was unable to process (save or relay) the incoming message, your server should notify the connected sender client of an error by rejecting the message. Pass an error object to the callback:
const SMTPServer = require('smtp-server').SMTPServer; const server = new SMTPServer({ onData: (stream, session, callback) => { // Failed to process the message callback(new Error('Oops, something went wrong')); } }); server.listen(25);
The recipient is passed in with the RCPT TO command (that address is included in the envelope) and as a message header. Check both values because they may differ.
SMTP does not have a mechanism for verifying the sender. It's similar to seeing "From" on a physical envelope.
When a client connects to your SMTP server, they exchange SMTP commands that include HELO, EHLO, DATA, and so on.
An email server listening on port 25 should support upgrading to TLS. SMTPServer supports it by default, and all you need to do is add key and cert to the options passed to the SMTPServer constructor:
const SMTPServer = require('smtp-server').SMTPServer; const server = new SMTPServer({ secure: false, key: fs.readFileSync('private.key'), cert: fs.readFileSync('server.crt'), onData: (stream, session, callback) => { // Process incoming messages } }); server.listen(25);
Notice that we also have secure: false
(it is false
by default). Setting secure
to true
makes the SMTP server only understand TLS and reject non-TLS. Email clients that support TLS still start with non-TLS when they connect to port 25.
This is similar to configuring an HTTPS server in Node.js. HTTPS will normally listen on port 443, and HTTP on port 80.
If your server receives an email addressed to someone else, you can choose to relay it (send it to the destination). This qualifies your email server as an open relay.
A simple online editor for MJML (Mailjet Markup Language).
How to safely create directories using the native file system module in Node.js?
How to generate data URLs in JavaScript?
What are cid URLs and how are they interpreted by email clients?
The difference between the email envelope and message headers when it comes to senders and recipients.
How to add npm dependencies from multiple package registries?
How do multipart emails work?
Building and training simple linear regression models in JavaScript using TensorFlow.js.
Parse, inspect, and debug emails online.
How to list all files recursively within a directory tree in Node.js?
All prices listed are in United States Dollars (USD). Visual representations of products are intended for illustrative purposes. Actual products may exhibit variations in color, texture, or other characteristics inherent to the manufacturing process.