<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Tomiwa Kunle Oluwadare</title>
    <description>The latest articles on DEV Community by Tomiwa Kunle Oluwadare (@tomiwa_oluwadare).</description>
    <link>https://dev.to/tomiwa_oluwadare</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3305424%2F5a0d60bc-8b76-4f51-a763-17f370059fcf.JPEG</url>
      <title>DEV Community: Tomiwa Kunle Oluwadare</title>
      <link>https://dev.to/tomiwa_oluwadare</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tomiwa_oluwadare"/>
    <language>en</language>
    <item>
      <title>Generating Custom Fillable PDFs with JavaScript and pdf-lib - Powered by MySQL Data</title>
      <dc:creator>Tomiwa Kunle Oluwadare</dc:creator>
      <pubDate>Sun, 29 Jun 2025 13:34:17 +0000</pubDate>
      <link>https://dev.to/tomiwa_oluwadare/generating-custom-fillable-pdfs-with-javascript-and-pdf-lib-powered-by-mysql-data-3lf5</link>
      <guid>https://dev.to/tomiwa_oluwadare/generating-custom-fillable-pdfs-with-javascript-and-pdf-lib-powered-by-mysql-data-3lf5</guid>
      <description>&lt;p&gt;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.&lt;br&gt;
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.&lt;/p&gt;
&lt;h2&gt;
  
  
  💡The Challenge
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;
&lt;h2&gt;
  
  
  🛠️ The Tools I Used
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;JavaScript (Node.js)&lt;/li&gt;
&lt;li&gt;pdf-lib for creating and editing PDFs&lt;/li&gt;
&lt;li&gt;MySQL for retrieving dynamic content&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  ⚙️ Step-by-Step Guide
&lt;/h2&gt;
&lt;h3&gt;
  
  
  📦 Step 1: Install Node.js and MySQL
&lt;/h3&gt;

&lt;p&gt;To run JavaScript server-side and connect to a database, you'll need Node.js and MySQL installed.&lt;/p&gt;
&lt;h4&gt;
  
  
  For Windows (Using Chocolatey)
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;choco install nodejs mysql -y

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;em&gt;Ensure Chocolatey is installed first.&lt;/em&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  For macOS (Using Homebrew)
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;brew install node mysql

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;em&gt;Make sure you have Homebrew installed.&lt;/em&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  For Linux (Debian/Ubuntu-based)
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -
sudo apt-get install -y nodejs mysql-server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;em&gt;Replace 18.x with your desired version.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Verify Installation
&lt;/h3&gt;

&lt;p&gt;After installation, verify by running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;node -v
npm -v
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  🗂️ Step 2: Set Up Your Project Directory
&lt;/h3&gt;

&lt;p&gt;Create your working folder and install the dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir pdf_generator &amp;amp;&amp;amp; cd pdf_generator
npm init -y
npm install pdf-lib mysql2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Please make sure that your package.json looks like this.&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "dependencies": {
    "mysql2": "^3.14.1",
    "pdf-lib": "^1.17.1"
  },
  "type": "module"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  🧬 Step 3: Prepare the DatabaseOpen up your terminal and type in or copy and paste this command to log in to MySQL client.
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Log into MySQL to create your database:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mysql -u root -p
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After a successful login attempt, create a database with the command provided below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; CREATE DATABSE fillable_pdf_form;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see something like this on your terminal after the database has been successfully created.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbfc8t0md3zjeki1gn3hm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbfc8t0md3zjeki1gn3hm.png" alt="Screenshot of a successful database changes" width="800" height="405"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Query OK, 1 row affected (0.003 sec)&lt;/code&gt;&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -O https://raw.githubusercontent.com/cybercoded/publication_projects/main/node/backup.sql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For you to import the backup data into your database, use this command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mysql -u root -p fillable_pdf_form &amp;lt; backup.sql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  🔌 Step 4: Connect to the Database (db_connection.js)
&lt;/h3&gt;

&lt;p&gt;Now, let's work on the JavaScript part, create a file named db_connection.js paste this code into the file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// 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
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  🧾 Step 5: Generate a Fillable PDF (pdf_generator.js)
&lt;/h3&gt;

&lt;p&gt;We have to create another file to do the PDF generation work by creating a file named &lt;code&gt;pdf_generator.js&lt;/code&gt; and paste the following code into the file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// 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);


}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The last step to get this running is to create the final file named &lt;code&gt;generate.js&lt;/code&gt; and paste this code into it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import getDataFromDB from './db_connection.js';
import generatePDF from './pdf_generator.js';

(async () =&amp;gt; {
  const data = await getDataFromDB(3); // ID 3 is just an example
  await generatePDF(data);
  console.log('PDF generated successfully.');
})();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;This is how files should be structured&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pdf_generator/
├── generate.js
├── db_connection.js
├── pdf_generator.js
├── package.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once this is done, go back to your terminal and run this command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;node generate.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxyf0q1hnqcosa0ub8gcy.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxyf0q1hnqcosa0ub8gcy.webp" alt="Screenshot of a successful fillable pdf generation" width="800" height="394"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;If you found this useful, feel free to ⭐️ the &lt;a href="https://github.com/cybercoded/publication_projects/tree/main/node" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; repo or connect with me on &lt;a href="https://linkedin.com/in/tomiwaoluwadare" rel="noopener noreferrer"&gt;Medium&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Would you like me to help you format this directly for Medium (e.g. images, code blocks, headings)?&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
