DEV Community

CodeZera
CodeZera

Posted on

How I Lost 10 Hours of My Weekend Solving This PDF Generation Nightmare (So You Don’t Have To!)

Let me start by introducing myself and explaining how this problem occurred in the first place.

I'm a full-stack web developer, with a stronger focus on the frontend. About a year ago, I joined a company that was working on a mortgage application website for a client. Before I arrived, there was only one developer working on that project, and he wrote all the code for generating PDFs using Puppeteer and Mustache. (If you don’t know what these are, continue reading; I’ll explain briefly.) He left the company after a while, and now I’m responsible for maintaining it.

As a frontend developer, I had no idea what Puppeteer was or why we even needed it.

Our backend was deployed on a platform called Railway, which provides a great experience for deploying Dockerized applications. (I think you can also deploy directly, and they handle the Dockerizing part for you, but it’s better to create a Dockerfile on your own.)

The Problem

Recently, a problem occurred on the website: we were sending a generated PDF from our backend when a user listed a mortgage application, but the emails were not getting delivered.

At first, I thought it might be related to the plan expiration with our email provider, Brevo. However, I tested it on my local machine with a solid email setup using Gmail's SMTP server. (If you want to know more about how this works, stay tuned; I’ll post a detailed blog on how to set this up for your projects.) To my relief, it worked perfectly.

I was almost certain that the issue was 100% related to our Brevo account, so I decided to enjoy my weekend. But then my manager, who oversees all the accounts, confirmed that the Brevo plan was not expired and that the problem was related to our backend.

I was really confused about what to do because everything was working fine on my local setup. I even tried changing the SMTP credentials in the production environment, but still no luck.

Then, I thought it might be that Railway was blocking outgoing emails for some reason. I raised a ticket on their forum to ask if they had recently started blocking outgoing SMTP traffic. One of their employees replied that Railway doesn’t block any outgoing traffic on any port.

Now, I was back to square one: the email provider was working fine, there was no problem with Railway, and everything was still functioning well on my local machine.

The Investigation

So, I did what any professional developer would do: I wrote a ton of console.log statements in my branch (literally one for each line, one before the variable initialization and one after, because I was just so frustrated about what was going wrong in Railway that was working on my local machine). I pushed everything to the testing environment in Railway.

After hours of checking the logs and confirming everything, I discovered that the problem was related to a package called Puppeteer. Now, let me explain what Puppeteer is and why it was used in my project.

What is Puppeteer?

Puppeteer is a JavaScript library that provides a high-level API to control Chrome or Firefox over the DevTools Protocol or WebDriver BiDi. Puppeteer runs in headless mode (no visible UI) by default. (This is directly from their official website; I also don’t fully understand how it works!)

To understand why it is needed, consider that there is no easy way to convert HTML to PDF. It is straightforward to generate an HTML page with some dynamic data using Mustache (you just pass it a base template and the data you want in the required format, and it creates an HTML page with all the data you provided).

To convert the rendered HTML to PDF, you need something called Puppeteer. It runs a browser inside your container, creates a new browser tab, and allows you to perform many operations you would do with that opened tab, such as taking screenshots or downloading the page as a PDF. So, it creates a page using the HTML data you provide and generates a PDF for you, which you can then save to any folder you like using the fs module.

Puppeteer offers many more features beyond this, but this is essentially what we used it for.


The Solution

Now, let’s get back to why this was causing a problem for me and how I figured it out. While checking my logs, I consistently saw that everything was working fine, but the code was always stuck at the log I wrote before launching Puppeteer.

After searching on Google and my favorite, Stack Overflow, I found out that the version of Puppeteer we were using had two issues:

  1. It was deprecated.
  2. It worked fine on local machines but had some weird issues when trying to launch a browser through it in Railway.

After hours of research, I came across a GitHub repository called Browserless. It essentially removes the overhead of launching a browser through Puppeteer. Puppeteer can connect to it and execute headless work all inside Docker.

To my luck, I also found a premade template for Railway that you can deploy with just one click on their platform. Here are the links to that template and a Puppeteer example:


Conclusion

In the world of web development(especially the devil JS), unexpected challenges can arise at any moment, often leading to frustrating experiences. My weekend was spent troubleshooting the PDF generation issue taught me valuable lessons about persistence, the importance of understanding the tools we use, and the power of community resources like GitHub and Stack Overflow. By using the right tools and resources, I was able to resolve this issue and ensure that our application functions smoothly.

If you find yourself facing similar challenges, remember that you're not alone. Don't hesitate to reach out for help, whether through forums, documentation, or fellow developers.

And lastly, if you're interested in learning how to create PDFs using Puppeteer and Mustache, drop a comment below, and I'll be happy to share a detailed guide!

Until next time, happy coding!


Top comments (0)