DEV Community

Cover image for Generating Custom Fillable PDFs with JavaScript and pdf-lib - Powered by MySQL Data
Tomiwa Kunle Oluwadare
Tomiwa Kunle Oluwadare

Posted on

Generating Custom Fillable PDFs with JavaScript and pdf-lib - Powered by MySQL Data

Automating the generation of fillable PDFs is a game-changer for handling documents like contracts, invoices, and client questionnaires. In one of my recent projects, I used pdf-lib,a powerful JavaScript library to dynamically populate and generate interactive PDF forms based on backend data.
In this guide, I'll walk you through how I achieved this automation using JavaScript and pdf-lib, including how to load an existing PDF, fill in fields programmatically, and export the result - all without requiring client-side interaction.

💡The Challenge

I was tasked with automating the completion of a multi-page, fillable PDF questionnaire that clients previously submitted via a webform. However, the client operates in a restricted environment without internet access, similar to how the PS5 restricts web browsing. While they couldn't access a webform, they could communicate via email, so we needed a system where the entire process from data retrieval from DB to PDF generation could be handled on server-side and the result would be emailed back.

🛠️ The Tools I Used

  • JavaScript (Node.js)
  • pdf-lib for creating and editing PDFs
  • MySQL for retrieving dynamic content

⚙️ Step-by-Step Guide

📦 Step 1: Install Node.js and MySQL

To run JavaScript server-side and connect to a database, you'll need Node.js and MySQL installed.

For Windows (Using Chocolatey)

choco install nodejs mysql -y

Enter fullscreen mode Exit fullscreen mode

Ensure Chocolatey is installed first.

For macOS (Using Homebrew)

brew install node mysql

Enter fullscreen mode Exit fullscreen mode

Make sure you have Homebrew installed.

For Linux (Debian/Ubuntu-based)

curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -
sudo apt-get install -y nodejs mysql-server
Enter fullscreen mode Exit fullscreen mode

Replace 18.x with your desired version.

Verify Installation

After installation, verify by running:

node -v
npm -v
Enter fullscreen mode Exit fullscreen mode

🗂️ Step 2: Set Up Your Project Directory

Create your working folder and install the dependencies:

mkdir pdf_generator && cd pdf_generator
npm init -y
npm install pdf-lib mysql2
Enter fullscreen mode Exit fullscreen mode

Please make sure that your package.json looks like this.

{
  "dependencies": {
    "mysql2": "^3.14.1",
    "pdf-lib": "^1.17.1"
  },
  "type": "module"
}
Enter fullscreen mode Exit fullscreen mode

🧬 Step 3: Prepare the DatabaseOpen up your terminal and type in or copy and paste this command to log in to MySQL client.

Log into MySQL to create your database:

mysql -u root -p
Enter fullscreen mode Exit fullscreen mode

After a successful login attempt, create a database with the command provided below.

 CREATE DATABSE fillable_pdf_form;
Enter fullscreen mode Exit fullscreen mode

You should see something like this on your terminal after the database has been successfully created.

Screenshot of a successful database changes

Query OK, 1 row affected (0.003 sec)

Now, just type exit to quit and go back to your terminal prompt.
The next thing is to download a dummy data curated for the purpose of the post using this command

curl -O https://raw.githubusercontent.com/cybercoded/publication_projects/main/node/backup.sql
Enter fullscreen mode Exit fullscreen mode

For you to import the backup data into your database, use this command

mysql -u root -p fillable_pdf_form < backup.sql
Enter fullscreen mode Exit fullscreen mode

🔌 Step 4: Connect to the Database (db_connection.js)

Now, let's work on the JavaScript part, create a file named db_connection.js paste this code into the file.

// db_connection.js
import mysql from 'mysql2/promise';

export default async function getDataFromDB(id = 1) {

  // Create a connection to the MySQL database
  const connection = await mysql.createConnection({
    host: 'localhost',
    database: 'fillable_pdf_form', 
    user: 'user_name', // replace with your own database username
    password: 'database_password' // replace with your own database password
  });

  // Fetch data from the database
  try {
    const [rows] = await connection.execute('SELECT * FROM reports WHERE id = ' + id);
    return rows[0];
  } finally {
    await connection.end(); // Closes the connection
  }
}
Enter fullscreen mode Exit fullscreen mode

🧾 Step 5: Generate a Fillable PDF (pdf_generator.js)

We have to create another file to do the PDF generation work by creating a file named pdf_generator.js and paste the following code into the file.

// pdf_generator.js
import { PDFDocument, StandardFonts } from 'pdf-lib';
import fs from 'fs';

export default async function generatePDF(data) {
  const pdfDoc = await PDFDocument.create();
  const page = pdfDoc.addPage([600, 800]);
  const form = pdfDoc.getForm();
  const font = await pdfDoc.embedFont(StandardFonts.Helvetica);

  const { title, summary, author } = data;

  page.drawText('Title:', { x: 50, y: 750, size: 12, font });
  page.drawText('Summary:', { x: 50, y: 700, size: 12, font });
  page.drawText('Author:', { x: 50, y: 650, size: 12, font });

  const titleField = form.createTextField('title');
  titleField.setText(title);
  titleField.addToPage(page, { x: 120, y: 740, width: 400, height: 20 });

  const summaryField = form.createTextField('summary');
  summaryField.setText(summary);
  summaryField.addToPage(page, { x: 120, y: 690, width: 400, height: 20 });

  const authorField = form.createTextField('author');
  authorField.setText(author);
  authorField.addToPage(page, { x: 120, y: 640, width: 400, height: 20 });

  const pdfBytes = await pdfDoc.save();
  fs.writeFileSync('report.pdf', pdfBytes);


}
Enter fullscreen mode Exit fullscreen mode

The last step to get this running is to create the final file named generate.js and paste this code into it.

import getDataFromDB from './db_connection.js';
import generatePDF from './pdf_generator.js';

(async () => {
  const data = await getDataFromDB(3); // ID 3 is just an example
  await generatePDF(data);
  console.log('PDF generated successfully.');
})();
Enter fullscreen mode Exit fullscreen mode

This is how files should be structured

pdf_generator/
├── generate.js
├── db_connection.js
├── pdf_generator.js
├── package.json
Enter fullscreen mode Exit fullscreen mode

Once this is done, go back to your terminal and run this command.

node generate.js
Enter fullscreen mode Exit fullscreen mode

Screenshot of a successful fillable pdf generation

Conclusion

Automating PDF generation can significantly reduce manual effort, especially in constrained environments. With pdf-lib, Node.js, and a simple database connection, you can easily build powerful form automation pipelines that integrate smoothly with your backend systems-even without client internet access.

If you found this useful, feel free to ⭐️ the GitHub repo or connect with me on Medium

Would you like me to help you format this directly for Medium (e.g. images, code blocks, headings)?

Top comments (0)