Tired of wondering if your emails are read or ignored? Let's build something cool. In this tutorial, we'll create a simple email tracking system using Node.js and Vercel. You'll learn to make an invisible image that reports when an email is opened. Ready? Let's dive in.
The core of this system is a Node.js Express server that serves a 1x1 transparent PNG image. When an email containing this image is opened, the recipient's email client sends a request to our server. This triggers our tracking mechanism, allowing us to log the event and serve the image. Here's a breakdown of the main components:
// Import required modules
const app = require('express')(); // Create an Express application
const { put } = require('@vercel/blob'); // Import the 'put' function from Vercel Blob for storing logs
const Jimp = require('jimp'); // Import Jimp for image manipulation
const path = require('path'); // Import path module for file path operations
// Enable trust proxy to get the correct IP address when behind a reverse proxy
app.set('trust proxy', true);
// Set the port for the server to listen on
const PORT = process.env.PORT || 3000;
// Middleware to log requests and store them in Vercel Blob
app.use(async (req, res, next) => {
const now = new Date(); // Get the current date and time
const imageName = path.parse(req.path).name; // Extract the filename (without extension) from the request path
const ip = req.ip; // Get the IP address of the client
const log = `${now.toISOString()} - ${imageName} - IP: ${ip}`; // Create a log entry
try {
// Store the log entry in Vercel Blob under the 'gmail' directory
await put(`gmail/${imageName}.txt`, log, {
access: 'public', // Make the log file publicly accessible
addRandomSuffix: false, // Do not add a random suffix to the filename
});
} catch (error) {
console.error("Error writing log to Vercel Blob:", error); // Log any errors that occur while writing the log
}
next(); // Continue to the next middleware or route handler
});
// Route to respond with a simple "Hello" message
app.get('/', (req, res) => {
res.send('Hello');
});
// Route to serve a 1x1 transparent PNG image
app.get('/image/:filename.png', (req, res) => {
const width = 1;
const height = 1;
// Create a new 1x1 transparent image using Jimp
new Jimp(width, height, 0x00000000, (err, image) => {
if (err) {
console.error("Error creating image:", err);
return res.status(500).send("Error creating image");
}
// Convert the image to a buffer and send it as the response
image.getBuffer(Jimp.MIME_PNG, (err, buffer) => {
if (err) {
console.error("Error getting image buffer:", err);
return res.status(500).send("Error processing image");
}
res.set('Content-Type', 'image/png'); // Set the response content type to PNG
res.send(buffer); // Send the image buffer as the response
});
});
});
// Start the server and listen on the specified port
app.listen(PORT, () => {
console.log(`Server started on port ${PORT}`);
});
Before you start coding, ensure that you have Node.js installed on your system. If not, you can download it from Node.js.
Next, install the necessary packages by running the following commands in your terminal:
npm install express
These packages include:
Jimp (JavaScript Image Manipulation Program) is used in this code to create a 1x1 transparent PNG image. Here's how it works:
const Jimp = require('jimp');
// Create a new 1x1 transparent image using Jimp
new Jimp(width, height, 0x00000000, (err, image) => {
// Error handling if image creation fails
if (err) {
console.error("Error creating image:", err);
return res.status(500).send("Error creating image");
}
// Convert the image to a PNG buffer
image.getBuffer(Jimp.MIME_PNG, (err, buffer) => {
// Error handling if buffer creation fails
if (err) {
console.error("Error getting image buffer:", err);
return res.status(500).send("Error processing image");
}
// Set the content type of the response to PNG
res.set('Content-Type', 'image/png');
// Send the PNG buffer as the response
res.send(buffer);
});
});
This approach allows us to serve a tiny, invisible image that can be used for tracking email opens without affecting the email's appearance.
Deploying your Node.js application on Vercel is straightforward. Here's the process:
npm install -g vercel
vercel.json
file to specify build and
routing
configurations. Here is an example:{
"version": 2, // Specifies the Vercel configuration schema version being used.
"builds": [
{
"src": "./app.js", // Path to the source file that Vercel will build.
"use": "@vercel/node" // Indicates that Vercel should use the Node.js runtime for this build.
}
],
"routes": [
{
"src": "/(.*)", // Pattern to match all incoming requests.
"dest": "/app.js" // All requests are routed to the app.js file.
}
]
}
vercel
vercel --prod
To store the logs of when emails are opened, I used Vercel's Blob storage. Here's how to set it up:
@vercel/blob
package by running the following command in
your terminal:
npm install @vercel/blob
put
function from the @vercel/blob
package to
store logs.
Here’s a code snippet demonstrating this:// Import the 'put' function from Vercel Blob for storing logs
const { put } = require('@vercel/blob');
// Store the log entry in Vercel Blob
await put(`logs/${imageName}.txt`, log, {
access: 'public', // Set the access level to public
addRandomSuffix: false, // Do not add a random suffix to the filename
});
Moving on to testing, we'll use Gmail to verify your Vercel deployment. Follow along:
https://your-vercel-app.vercel.app/image/uniqueimagename.png
Replace `your-vercel-app` with your actual Vercel app name, and `uniqueimagename` with a name of your choice.
This tracking method uses an invisible 1x1 PNG image in your email. When opened, it silently contacts your server, providing engagement data without altering the email's appearance or alerting recipients. This allows accurate measurement of open rates and timing while preserving user experience.
Note: Remember that this method isn't 100% reliable as some email clients block images by default or give users the option to not load images. Additionally, be sure to comply with all relevant privacy laws and regulations when implementing email tracking.
With these steps, We successfully created and deployed a simple email tracking system using Node.js and Vercel. The key to this project was understanding how to serve and log image requests, as well as effectively deploying the server to Vercel. I hope this guide helps you in building your own email tracking systems or similar projects!
As an upcoming feature, We plan to add functionality "receive SMS notifications on your phone when someone opens your email". This will provide real-time alerts and further enhance the tracking capabilities of the system. Stay tuned for updates on how this SMS integration will be implemented!