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
Ensure Chocolatey is installed first.
For macOS (Using Homebrew)
brew install node mysql
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
Replace 18.x with your desired version.
Verify Installation
After installation, verify by running:
node -v
npm -v
🗂️ 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
Please make sure that your package.json looks like this.
{
"dependencies": {
"mysql2": "^3.14.1",
"pdf-lib": "^1.17.1"
},
"type": "module"
}
🧬 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
After a successful login attempt, create a database with the command provided below.
CREATE DATABSE fillable_pdf_form;
You should see something like this on your terminal after the database has been successfully created.
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
For you to import the backup data into your database, use this command
mysql -u root -p fillable_pdf_form < backup.sql
🔌 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
}
}
🧾 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);
}
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.');
})();
This is how files should be structured
pdf_generator/
├── generate.js
├── db_connection.js
├── pdf_generator.js
├── package.json
Once this is done, go back to your terminal and run this command.
node generate.js
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)