Chris
Posted on September 5, 2023
Today, I'll walk you through the process of generating an invoice PDF file for your backend app or custom script which could be run from the command line. Let's get started! š
Introduction
Node.js programming language will be used for simplicity.
Handlebars template engine to separate data from the presentation.
html2pdf.app to convert HTML to PDF, but as an alternative Puppeteer can be used also (you can find a complete tutorial How to convert HTML to PDF with puppeteer).
Project structure
Let's assume we are creating a new project "generate-invoice", first Create a directory using the command mkdir generate-invoice
. Then initialize a Node.js project by entering the command npm init
and provide all the necessary information.
Now we need to install dependencies, which will be used. For that run the command npm i handlebars axios
.
Having all the needed dependencies, let's create a conversion file using the command mkdir src && touch src/generate.js
.
Now we need to store the template where the HTML code will be provided. Create a templates directory and invoice.html file for that with the command mkdir templates && touch templates/invoice.html
.
This simple project structure is enough to start writing the code. Your project should look like this tree below.
āāā package-lock.json
āāā package.json
āāā node_modules
āāā src
ā āāā generate.js
āāā templates
āāā invoice.html
Prepare the template
Before diving into the conversion process, you'll need to create an HTML template for your invoice. This template will define the layout, content, and styling of your PDF invoice.
There are many invoice HTML templates you can find on Google. I am going to use an already created one, whose code source you can find here
In the end, it should look the following:
Feel free to customize the HTML template to match your branding and invoicing needs. You can update fonts, and colours, add your company logo, and include any necessary details like invoice number, customer information, etc.
Put your template HTML code into the templates/invoice.html
file and let's move to the next step.
Convert HTML to PDF
First, we need to add needed dependencies to the conversion script.
import fs from 'fs';
import Handlebars from 'handlebars';
import axios from 'axios';
Now we can load the invoice HTML code into the Handlebars template.
const invoice = fs.readFileSync('./assets/invoice.html', 'utf8');
const template = Handlebars.compile(invoice);
Also, we need to set parameters which will be binded into the template, they can come from various places in your real project. For example database, Excel file, command line arguments and so on. I will define those parameters in the script directly for simplicity.
const params = {
invoiceNr: 'H2P202307-01711',
billTo: 'Anna Smith',
invoiceDate: (new Date()).toDateString(),
paymentDue: (new Date()).toDateString(),
orderId: 112233,
items: [
{description: 'Domain registration', quantity: 2, price: '$10', total: '$20'},
{description: 'Web hosting', quantity: 1, price: '$15', total: '$15'},
{description: 'Consulting', quantity: 1, price: '$500', total: '$500'},
],
subTotal: '$535',
tax: '$53.5',
total: '$588.5',
};
Now we can process the template by passing the parameters and get the result - the final HTML code which will be converted to the PDF document.
const html = template(params);
There are multiple options for how to convert HTML to PDF, one could be by using open-source projects like Puppeteer or wkhtmltopdf. I wrote a separate post How to convert HTML to PDF using Puppeteer, but now for simplicity, I going to use html2pdf.app. Its free plan gives 100 credits per month, excellent!
To register and get the free API key use the link: https://dash.html2pdf.app/registration
Once you have an API Key, let's finish the conversion part.
const apiKey = '******************'; // API key from https://html2pdf.app
const response = await axios.post('https://api.html2pdf.app/v1/generate', {
html,
apiKey,
}, {responseType: 'arraybuffer'});
To send the HTML code to the PDF conversion API there are only a few lines of code. Now what needs to be done is to save the response. For that add the code line below to your script.
fs.writeFileSync('./assets/invoice.pdf', response.data);
The complete conversion script code looks as follows:
import fs from 'fs';
import Handlebars from 'handlebars';
import axios from 'axios';
const invoice = fs.readFileSync('./assets/invoice.html', 'utf8');
const template = Handlebars.compile(invoice);
const params = {
invoiceNr: 'H2P202307-01711',
billTo: 'Anna Smith',
invoiceDate: (new Date()).toDateString(),
paymentDue: (new Date()).toDateString(),
orderId: 112233,
items: [
{description: 'Domain registration', quantity: 2, price: '$10', total: '$20'},
{description: 'Web hosting', quantity: 1, price: '$15', total: '$15'},
{description: 'Consulting', quantity: 1, price: '$500', total: '$500'},
],
subTotal: '$535',
tax: '$53.5',
total: '$588.5',
};
const html = template(params);
const apiKey = 'd3a0264f70864...'; // get api key on https://html2pdf.app
const response = await axios.post('https://api.html2pdf.app/v1/generate', {
html,
apiKey,
}, {responseType: 'arraybuffer'});
fs.writeFileSync('./assets/invoice.pdf', response.data);
You can find it on GitHub also https://github.com/html2pdfapp/examples/blob/main/src/generate-invoice.js
Conclusion
I like not to mix business logic or data with the presentation layer, and currently, there is a much clearer solution when we have a template separated from the data.
The simplicity of html2pdf.app API and its free plan allow us to save time and only with a few lines of code, we have the converted PDF file you can store it somewhere in the cloud or project directory.
Happy coding!
Posted on September 5, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.