Search code examples
node.jsapiexpresspdfmake

How to create a PDF and download it when the endpoints is requested by client


I'm building an API in NodeJS and Express. IT is about a marketplace for products. I recently started to implement the PKG called PDFMake but I have issues to make it work. What I'm trying to do is that when I hit the endpoint URL/api/v1/products/:id/exportToPdf I should create the PDF with the product info and downloaded it. This is not working in postman I see an error "message": "The value \"product.pdf\" is invalid for option \"encoding\"" I cannot find a solution to what I want to achieve and need help to understand how to resolve it.

I'm sharing the parts working with it:

Router:

// GET one Product and Export to PDF
router.get("/:id/exportToPDF", check.isValidId, check.rules, async (req, res) => {
    const id = req.params.id;
    const description = req.body.description;
    const name = req.body.name;
    const brand = req.body.brand;
    const category = req.body.category;
    const price = req.body.price;
    await product
        .getOneProductExportToPdf(id, description, name, brand, category, price)
        .then(data =>
            // OK
            res.status(201).json({
                message: `PDF Created`,
                content: data
            })
        )
        .catch(err => {
            if (err.status) {
                res.status(err.status).json({ message: err.message });
            } else {
                res.status(500).json({ message: err.message });
            }
        });
});

Model

// GET One Product PDF Export
const getOneProductExportToPdf = (id, description, name, brand, category, price) => {
    return new Promise((resolve, reject) => {
        helper
            .mustBeInArray(products, id)
            .then(product => {
                const dd = {
                    content: [
                        { text: "Product Info", style: "header" },
                        { text: `${id}`, style: "subheader" },
                        { text: `${description}`, style: "subheader" },
                        { text: `${name}`, style: "subheader" },
                        { text: `${brand}`, style: "subheader" },
                        { text: `${category}`, style: "subheader" },
                        { text: `${price}`, style: "subheader" }
                    ]
                };

                var fonts = {
                    Roboto: {
                        normal: "Helvetica",
                        bold: "Helvetica-Bold",
                        italics: "Helvetica-Oblique",
                        bolditalics: "Helvetica-BoldOblique"
                    }
                };

                const printer = new PdfPrinter(fonts);

                const pdfDoc = printer.createPdfKitDocument(dd, {})

                pdfDoc.pipe(
                    fs.createReadStream(f.uploadsDir, "product.pdf")
                )

                pdfDoc.end();

                resolve(pdfDoc);
            })
            .catch(err => reject(err));
    });
};

The f.uploadsDir is just pointing to the folder where I want to upload on the server the PDF


Solution

  • // GET one Product and Export to PDF
    router.get("/:id/exportToPDF", check.isValidId, check.rules, async (req, res) => {
        const id = req.params.id;
        const description = req.body.description;
        const name = req.body.name;
        const brand = req.body.brand;
        const category = req.body.category;
        const price = req.body.price;
        await product
            .getOneProductExportToPdf(id, description, name, brand, category, price)
            .then(pdfDoc =>{
                // Will get downloaded
                res.setHeader('Content-Type', 'application/pdf');
                res.setHeader('Content-Disposition', 'attachment; filename=product.pdf');
                pdfDoc.pipe(res);
                pdfDoc.end();
            })
            .catch(err => {
                if (err.status) {
                    res.status(err.status).json({ message: err.message });
                } else {
                    res.status(500).json({ message: err.message });
                }
            });
    });
    
    // GET One Product PDF Export
    const getOneProductExportToPdf = (id, description, name, brand, category, price) => {
        return new Promise((resolve, reject) => {
            helper
                .mustBeInArray(products, id)
                .then(product => {
                    const dd = {
                        content: [
                            { text: "Product Info", style: "header" },
                            { text: `${id}`, style: "subheader" },
                            { text: `${description}`, style: "subheader" },
                            { text: `${name}`, style: "subheader" },
                            { text: `${brand}`, style: "subheader" },
                            { text: `${category}`, style: "subheader" },
                            { text: `${price}`, style: "subheader" }
                        ]
                    };
    
                    var fonts = {
                        Roboto: {
                            normal: "Helvetica",
                            bold: "Helvetica-Bold",
                            italics: "Helvetica-Oblique",
                            bolditalics: "Helvetica-BoldOblique"
                        }
                    };
    
                    const printer = new PdfPrinter(fonts);
    
                    const pdfDoc = printer.createPdfKitDocument(dd, {})
    
                    resolve(pdfDoc);
                })
                .catch(err => reject(err));
        });
    };
    

    It will not create product.pdf in the server, instead stream the pdf directly in the response. Also use 'Save Response' in postman to download the pdf file (in case you can't preview the response)