Mailjet is an email delivery service. In this post I will show how we can use this platform to deliver programatically generated PDFs via email. To achieve that we’re going to use Node.js together with following libraries:

  • node-mailjet – as a wrapper over the Mailjet API
  • pdfkit – to generate PDF files
  • base64-stream – to convert generated PDF into a Base64 string which is required by Mailjet

First let’s write a function to generate a Base64 encoded PDF file using PDFKit.

import PDFDocument from "pdfkit";
import { Base64Encode } from "base64-stream";

async function generatePdf(userName: string): Promise<string> {
  // we wrap everything with a Promise to make a nice interface 
  // to just await a PDF content
  return new Promise((resolve, reject) => { 
    console.time("pdf"); // we can track how long it took to generate a PDF
    const doc = new PDFDocument();
    const stream = doc.pipe(new Base64Encode());
    let finalString = ""; // we collect the actual stream data in this variable
    doc
      .fontSize(24) // we can configure different font size for the first line
      .text("Order confirmation")
      .lineGap(1)
      .fontSize(16)
      .text(`Hello ${userName}. We just received your order`)
      .end(); // this will trigger the "end" event on the stream

    stream.on("data", (chunk) => {
      finalString += chunk;
    });

    stream.on("end", () => {
      console.timeEnd("pdf"); // on my machine it took 32ms
      resolve(finalString);
    });

    stream.on("error", (err) => {
      reject(err);
    });
  });
}

The result is going to look something like this:

JVBERi0xLjMKJf////8KNyAwIG9iago8PAovVHlwZSAvUGFnZQovUGFyZW50IDEgMCBSCi9NZWRpYUJveCBbMCAwIDYxMiA3OTJdCi9Db250ZW50cyA1IDAgUgovUmVzb3VyY2VzIDYgMCBSCj4+CmVuZG9iago2IDAgb2JqCjw8Ci9Qcm9jU2V0IFsvUERGIC9UZXh0IC9JbWFnZUIgL0ltYWdlQyAvSW1hZ2VJXQovRm9udCA8PAovRjEgOCAwIFIKPj4KPj4KZW5kb2JqCjUgMCBvYmoKPDwKL0xlbmd0aCAxODQKL0ZpbHRlciAvRmxhdGVEZWNvZGUKPj4Kc3RyZWFtCnicjY+xDsIwDET3fIV/AHDc+NxKVQYkGNiQsiGGqjRbB/5/wYUKgViQpZxzsXLPkdhrE/2wTmicwz3EH29fVjOSCRnL1tBSmcPuGEkSlRoufaomSFATYTSomAB0Jpk2otTjhmgJ3fKQia9UTuFQwvmPQLT8GRixBrZQjF5VOA3+b4vJu+SdZoqeKZ4EduVMyVUtU+MKdcTB1BpLwo6tDqwOi0xPVsXid35bpqu9lnov+MX/ADwdQ6AKZW5kc3RyZWFtCmVuZG9iagoxMCAwIG9iagooUERGS2l0KQplbmRvYmoKMTEgMCBvYmoKKFBERktpdCkKZW5kb2JqCjEyIDAgb2JqCihEOjIwMjIwODEyMTEyNDQ2WikKZW5kb2JqCjkgMCBvYmoKPDwKL1Byb2R1Y2VyIDEwIDAgUgovQ3JlYXRvciAxMSAwIFIKL0NyZWF0aW9uRGF0ZSAxMiAwIFIKPj4KZW5kb2JqCjggMCBvYmoKPDwKL1R5cGUgL0ZvbnQKL0Jhc2VGb250IC9IZWx2ZXRpY2EKL1N1YnR5cGUgL1R5cGUxCi9FbmNvZGluZyAvV2luQW5zaUVuY29kaW5nCj4+CmVuZG9iago0IDAgb2JqCjw8Cj4+CmVuZG9iagozIDAgb2JqCjw8Ci9UeXBlIC9DYXRhbG9nCi9QYWdlcyAxIDAgUgovTmFtZXMgMiAwIFIKPj4KZW5kb2JqCjEgMCBvYmoKPDwKL1R5cGUgL1BhZ2VzCi9Db3VudCAxCi9LaWRzIFs3IDAgUl0KPj4KZW5kb2JqCjIgMCBvYmoKPDwKL0Rlc3RzIDw8CiAgL05hbWVzIFsKXQo+Pgo+PgplbmRvYmoKeHJlZgowIDEzCjAwMDAwMDAwMDAgNjU1MzUgZiAKMDAwMDAwMDgwNSAwMDAwMCBuIAowMDAwMDAwODYyIDAwMDAwIG4gCjAwMDAwMDA3NDMgMDAwMDAgbiAKMDAwMDAwMDcyMiAwMDAwMCBuIAowMDAwMDAwMjA4IDAwMDAwIG4gCjAwMDAwMDAxMTkgMDAwMDAgbiAKMDAwMDAwMDAxNSAwMDAwMCBuIAowMDAwMDAwNjI1IDAwMDAwIG4gCjAwMDAwMDA1NTAgMDAwMDAgbiAKMDAwMDAwMDQ2NCAwMDAwMCBuIAowMDAwMDAwNDg5IDAwMDAwIG4gCjAwMDAwMDA1MTQgMDAwMDAgbiAKdHJhaWxlcgo8PAovU2l6ZSAxMwovUm9vdCAzIDAgUgovSW5mbyA5IDAgUgovSUQgWzwxMWI2ZjBkMGUzYWY2ZmNhNGQ0NTc5Y2RjZTMyZWNhYj4gPDExYjZmMGQwZTNhZjZmY2E0ZDQ1NzljZGNlMzJlY2FiPl0KPj4Kc3RhcnR4cmVmCjkwOQolJUVPRgo=

Now that we have a function to generate a PDF we can use Mailjet API to send the email:

import Mailjet from "node-mailjet";

// ...

async function main(sendToEmail: string, sendToName: string) {
  const apiKey = process.env.MAILJET_API_KEY as string;
  const apiSecret = process.env.MAILJET_API_SECRET as string; 
  const mailjet = Mailjet.apiConnect(apiKey, apiSecret);

  const pdfContent = await generatePdf("John Doe");
  await mailjet.post("send", { version: "v3.1" }).request({
    Messages: [
      {
        From: {
          Email: "delivery.system@yourcompany.com",
          Name: "Delivery",
        },
        To: [
          {
            Email: sendToEmail,
            Name: sendToName,
          },
        ],
        Subject: "Order confirmation",
        TextPart:
          "Dear receiver. Here's a confirmation or your order",
        Attachments: [
          {
            ContentType: "application/pdf",
            FileName: "confirmation.pdf",
            Base64Content: pdfContent,
          },
        ],
      },
    ],
  });
}

And if we call the function and check our email we can see the email looks like on the screenshots below:

That’s how it looks in the email client
And that’s the actual PDF file

Author

I'm a software engineer with 9 years of experience. I highly value team work and focus a lot on knowledge sharing aspects within teams. I also support companies with technical interview process. On top of that I read psychological books in my spare time and find other people fascinating.