Building a PDF invoice generator doesn't have to be a complex and time-consuming task. Creating a serverless function for generating PDF invoices is a great use case for serverless computing. Read on to see how you can leverage the power of serverless computing to employ a minimalist approach using readily available tools and technologies.
At the forefront of this serverless revolution is Appwrite, an open-source platform that leverages the power of serverless functions to create scalable, efficient, and cost-effective solutions.
Their Appwrite Functions feature enables developers to create and automatically run custom backend code in response to events triggered by Appwrite or according to a predefined schedule. With Appwrite Functions, we can access several benefits, including scalability and enhanced security.
In this tutorial, we'll showcase the Appwrite Generate PDF Function template, show you how to set it up, and guide you through the swift and straightforward process of building a PDF invoice generator in just a few minutes.
By the end of this tutorial, we'll have a functional PDF invoice generator that can be easily integrated into your applications or workflows, making the traditionally complex task of invoice generation a quick and efficient process.
GitHub
Check out the complete source code here.
Prerequisites
To follow along with this tutorial comfortably, you’ll need a few things:
- Basic knowledge of Javascript and Svelte
- An Appwrite Cloud account
- A GitHub account
Getting started
In this tutorial, we'll set up a frontend demo app. The demo app will be a Svelte invoice app that'll send data from the invoice input form to the Appwrite PDF Functions — cool, right?
To set up our application, clone this repository using the command below:
git clone https://github.com/Tundesamson26/svelte-invoice-generator.git
Then, run the command below in the project directory:
$ cd svelte-invoice-generator
$ npm install
This repository contains all the initial setups we'll need for our frontend app, helping us focus on our app's main functionality.
To use Appwrite in our Svelte application, install the Appwrite client-side SDK for web applications.
npm install appwrite
Then, run the development server using the command below:
npm run dev
Once the development server is running, we can view it on our browser at http://localhost:5173.
Creating Appwrite Functions
To integrate Appwrite Functions, we must create a project on Appwrite’s console. To do this, log into the Appwrite console, click the Create project button, name the project appwrite-invoice-pdf
, and click Create.
Creating an Appwrite Function using the Generate PDF template
Here, we'll easily create an Appwrite Function to automate our backend tasks and extend Appwrite with custom code.
Let's navigate to the side menu on our project dashboard and click on Functions to access the template:
Click on the Templates tab on the Functions page:
Scrolling down, you’ll see the Generate PDF template; click on the Create function button as shown below:
We'll select Node.js-18.0 as the runtime for our PDF Invoice generator and click Next:
For the Appwrite’s Generate PDF Functions, no environment variables are required, so we’ll proceed by clicking on Next.
Getting to the next step of connecting our Appwrite Functions to a new repository or an existing one within a selected Git organization. Select Create a new repository and click Next:
Next, we’ll have the GitHub organization and repository name created for us automatically, as shown below. Let’s leave our repository private and click Next. Afterward, the repository will be successfully created.
Note: You can change the repository name to anything of your choosing.
Next, we'll name our production branch or leave it as main, then click the Create button
After completing the configurations, an active Appwrite function should be deployed, as shown below.
Completing the above configuration will create a new Appwrite function folder in our repository.
Glancing through the deployed source code, you’ll notice three files: main.js
, faker.js
, and pdf.js
. For brevity, we’ll highlight two essential files here, namely:
-
main.js
file: This file in thefunctions/src
folder houses a function to interact with the Svelte app invoice data -
pdf.js
file: This file is also inside thefunctions/src
folder that’ll be used to construct our PDF file
Next, navigate to the Domains tab from the Functions page and click on the verified generated URL.
Clicking on the verified generated URL will take us to the fake data from the Appwrite Generate PDF functions template.
Adding functionality to the invoice frontend
We will start our logic by adding a function that sends user-inputted data to our Appwrite Functions and generates a PDF document based on the input from our Svelte client side.
Navigate to the App.svelte
file and add the code as shown below:
<script>
import { Client, Functions } from "appwrite";
const client = new Client()
.setEndpoint("https://cloud.appwrite.io/v1")
.setProject("[PROJECT_ID]");
const functions = new Functions(client);
// Function to send data to the serverless function
const generatePdf = async () => {
const data = {
name: $name,
address: $address,
email: $email,
phone: $phone,
bankName: $bankName,
bankAccount: $bankAccount,
website: $website,
clientName: $clientName,
clientAddress: $clientAddress,
invoiceNumber: $invoiceNumber,
invoiceDate: $invoiceDate,
dueDate: $dueDate,
notes: $notes,
list: $list,
};
try {
let response = await functions.createExecution(
"[FUNCTION_ID]",
JSON.stringify({
data
}),
false,
"/https://655f28d1449b15f23a3a.appwrite.global/",
"POST",
{
"Content-Type": "application/json",
}
);
let pdfString = response.responseBody;
let blob = b64toBlob(pdfString, "application/pdf");
const blobUrl = URL.createObjectURL(blob);
console.log(blobUrl);
window.open(blobUrl);
} catch (error) {
console.error("Error:", error.message);
}
};
// Function to convert pdfstring to blob
const b64toBlob = (b64Data, contentType = "", sliceSize = 512) => {
const byteCharacters = atob(b64Data);
const byteArrays = [];
for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) {
const slice = byteCharacters.slice(offset, offset + sliceSize);
const byteNumbers = new Array(slice.length);
for (let i = 0; i < slice.length; i++) {
byteNumbers[i] = slice.charCodeAt(i);
}
const byteArray = new Uint8Array(byteNumbers);
byteArrays.push(byteArray);
}
const blob = new Blob(byteArrays, { type: contentType });
return blob;
};
</script>
In the code above, we:
- Import the Appwrite library and initialize an Appwrite client with the
endpoint
andproject ID
. We get theproject ID
from our Appwrite dashboard by clicking on the Overview tab. - Create instances of the Account and Functions classes using the initialized client. Appwrite's Account class allows us to set up an anonymous user, providing a convenient means to test the functionality of our Appwrite function without the need to implement a custom authentication system.
- Define a
generatePdf
function that gathers invoice data from the Svelte store, formats it into a payload, and it to the Appwrite functions. - Call the
functions.createExecution
method to invoke the Appwrite serverless function. It takes several parameters:-
[FUNCTION_ID]
: The ID of the serverless function to be executed. -
JSON.stringify({ data })
: The data object is converted to a JSON string and sent as the payload to the serverless function. -
false
: This parameter indicates whether the function should execute asynchronously (in the background). In this case, it is set to false, suggesting the functions will execute synchronously. -
/https://655f28d1449b15f23a3a.appwrite.global/
: The endpoint where the serverless function is hosted. -
POST
: The HTTP method used for the request. -
{ "Content-Type": "application/json" }
: The headers for the HTTP request specifying that the payload is in JSON format.
-
- Define a
b64toBlob
function that takes base64-encoded datab64Data
, an optional content typecontentType
, and a slice size as parameters and returns aBlob
.
Next, add a Generate PDF button to the HTML template to trigger the generatePdf
function.
<button class="mt-5 bg-green-500 py-2 px-8 text-white font-bold shadow border-2 border-green-500 hover:bg-transparent hover:text-green-500 transition-all duration-300"
on:click={generatePdf}>
Generate PDF
</button>
We'll now focus on altering the main.js
and pdf.js
files to achieve the app's functionality.
Modifying the generated Functions code on GitHub
Open the source code and navigate to the src/main.js
file. Then, edit the file as shown below:
import { createPdf } from "./pdf.js";
export default async ({ res, req, log, error }) => {
if (req.method === "POST") {
try {
const payload = (req.body.data);
const pdfBuffer = await createPdf(payload);
const pdfBase64 = pdfBuffer.toString('base64');
return res.send(pdfBase64, 200, { "Content-Type": "application/pdf" });
} catch (err) {
log('Error processing the request: ' + err.message);
return res.send('Internal Server Error ' + err.message);
}
} else {
log(error)
return res.send('Bad Request');
}
};
The code above did the following:
- Import the
createPdf
function fromsrc/pdf.js
file. - It expects
POST
requests, extracts payload data, generates a PDF using thecreatePdf
function, and responds with the generated PDF in base64 format. If there's any error, it logs it and sends an error response. If the request method is notPOST
, it logs an error and sends a Bad Request response.
Next, we need to structure our data in an arranged PDF format. Navigate to the src/pdj.js
file and edit the code as shown below:
The code above is organized to create a visually structured invoice with information such as invoice date, due date, invoice number, billing details, line items, and additional notes. The pdf-lib library handles PDF generation and renders text on pages.
Demo
The app should function like this demo after applying the necessary configurations above:
Conclusion
This post discussed the integration of an Appwrite Function template, the Generate PDF template, in a Svelte application. We addressed the importance of adopting the Appwrite Functions template, which fast-tracks productivity by making the traditionally complex task of invoice generation a quick and efficient process.
The tutorial not only emphasized the straightforward setup of the template but also presented a practical demonstration of its integration in a real-world scenario. This hands-on example is a valuable reference for developers seeking to implement similar functionalities in their applications.
The Generate PDF function template from Appwrite is flexible and can be easily customized to suit your needs. Developers can customize the template to accept payload from our frontend app. For instance, in the case of the invoice generator we built in this article, you can get multiple data points from our invoice form and quickly generate a PDF in minutes for business and individual use. The ability to tailor the function to particular needs can significantly improve the process of adding features to your applications, ultimately saving time and effort.
Resources
These resources may also be helpful: