DEV Community

Gabriel de Paula Queiroz
Gabriel de Paula Queiroz

Posted on

GENERATE PDF with Puppeteer + Handlebars

In this tutorial, we are going to learn how to generate a PDF using an API.

For this, we will use the following libs

Let's use the lib https://www.npmjs.com/package/puppeteer, which is a chromium for us to manipulate our HTML

We will also use the Handlebars lib https://www.npmjs.com/package/handlebars, which is basically a template compiler.

So the first step is to install these libraries, for that just run



npm install puppeteer handlebars

// or if you are using yarn

yarn install puppeteer handlebars


Enter fullscreen mode Exit fullscreen mode

After installing the libraries, we will create a controller in our API by starting the puppeteer configuration.



class PDFController {

async index(_: Request, response: Response) {
    // Defines the name of the PDF
    const name = 'nodejs-pdf-example.pdf';

    let configLaunch = {
        headless: true, // headless: 
Informs whether the browser should run headless, that is, without a graphical interface
        ignoreDefaultArgs: ['--disable-extensions'],
    };

    // Start the puppeteer
    const browser = await puppeteer.launch(configLaunch);

    // Open new page
    const page = await browser.newPage();
    const waitUntil = 'networkidle2';
}

export default new PDFController();


Enter fullscreen mode Exit fullscreen mode

Now, let's add our HTML, which will serve as the base of our PDF



...
    // getting the template
    const templateDir = resolve(__dirname, '..', 'views', 'template-pdf.hbs');
    const file = fs.readFileSync(templateDir, 'utf-8');

    // compiling
    const fileCompiled = Handlebars.compile(file);

    // getting the template in string
    const fileHTML = fileCompiled({})
... 



Enter fullscreen mode Exit fullscreen mode

After that, let's integrate our HTML with the puppeteer. The puppeteer has his own method to generate the pdf.



    await page.setContent(fileHTML, {
        waitUntil,
    });

    await page.setDefaultNavigationTimeout(0);

    // generate PDF
    await page.pdf({
        format: 'A4',
        path: `tmp/${name}`,
        displayHeaderFooter: false,
        preferCSSPageSize: false,
        printBackground: true,
    });

    await browser.close();
    const pdfFile = fs.readFileSync(`tmp/${name}`);

    // Removing PDF file from the temp folder
    fs.unlinkSync(`tmp/${name}`);

    // return the PDF
    response.contentType('application/pdf');
    response.send(pdfFile);


Enter fullscreen mode Exit fullscreen mode

And the complete code should look like this:



import { Request, Response } from "express";
import puppeteer from "puppeteer";
import fs from 'fs'
import { resolve } from "path";
import Handlebars from "handlebars";

class PDFController {
async index(_: Request, response: Response) {
    const name = 'nodejs-pdf-example.pdf';

    let configLaunch = {
        headless: true, 
        ignoreDefaultArgs: ['--disable-extensions'],
    };

    const browser = await puppeteer.launch(configLaunch);

    const page = await browser.newPage();
    const waitUntil = 'networkidle2';

    const templateDir = resolve(__dirname, '..', 'views', 'template-pdf.hbs');
    const file = fs.readFileSync(templateDir, 'utf-8');
    const fileCompiled = Handlebars.compile(file);
    const fileHTML = fileCompiled({})

    await page.setContent(fileHTML, {
        waitUntil,
    });

    await page.setDefaultNavigationTimeout(0);

    await page.pdf({
        format: 'A4',
        path: `tmp/${name}`,
        displayHeaderFooter: false,
        preferCSSPageSize: false,
        printBackground: true,
    });

    await browser.close();
    const pdfFile = fs.readFileSync(`tmp/${name}`);

    fs.unlinkSync(`tmp/${name}`);

    response.contentType('application/pdf');
    response.send(pdfFile);
}

export default new PDFController();


Enter fullscreen mode Exit fullscreen mode

folders

Hope I was able to help!

The full project directory is at: https://github.com/gabrielqueirozdev/nodejs-pdf-example

If you have any suggestions for improvement, please contact me.

👋🏼

Top comments (0)