Generate a Pdf with Google Cloud Functions

Ivan Benegiamo
May 5, 2018 · 5 min read

Are you a google cloud developer? Are you curious about serverless architecture? You should dive into this article!

Google Cloud Function is event-driven serverless compute platform and allow you to develop a service without the need to manage a server or runtime environment, in few words without taking care about the infrastructure where the function is running, this means you have to concentrate in writing code of your function, nothing else!

In this specific case, we create a cloud function that processes JSON data and creates a pdf.

Google Cloud Functions are written in JavaScript, and executed in a Node.js runtime. We’ve used a javascript library called pdfmake that is a library based on pdfkit that is also available as a nodejs module. You can use it client side and server side as well. Let’s go!

First, create your package.json file through “npm init” command and put inside the following packages as dependencies

“dependencies”: {“@google-cloud/debug-agent”: “².4.0”,“@google-cloud/storage”: “¹.6.0”,“googleapis”: “21.2”,“node-fetch”: “².1.2”,“pdfmake”: “⁰.1.36”,“request”: “².85.0”,“uuid”: “³.2.1”}

And after type “npm install” to install all dependencies

Create your index.js file in which we’re going to define our business logic of the cloud function

/*** Cloud Function.** @param {Object} req Cloud Function request context.* @param {Object} res Cloud Function response context.*/exports.yourFunctionName = function yourFunctionName(req, res) {console.log(‘this is the request’);console.log(req);var accessToken = getAccessToken(req.get(‘Authorization’));var oauth = new Google.auth.OAuth2();oauth.setCredentials({access_token: accessToken});var permission = ‘storage.buckets.get’;var gcs = Google.storage(‘v1’);gcs.buckets.testIamPermissions({bucket: BUCKET, permissions: [permission], auth: oauth}, {},function (err, response) {if (response && response[‘permissions’] && response[‘permissions’].includes(permission)) {authorized(res);} else {console.log(‘ — -Error below — -’);console.log(err);res.status(403).send(“The request is forbidden.”);}});};

authorized(res) is a function that is executed if the access token provided in the request is correct and it is implemented in this way

function authorized(res) {createPDF().then(function(file_name){res.status(200).send("The request was successfully authorized and pdf generated.\n You can find your pdf in the cloud storage " + file_name);}).catch( function(error) {console.error("Failed!" + error);res.status(400).send("Error: Pdf generation failed!");});}

createPDF() is the function in charge to create the pdf file and store it on cloud storage

function createPDF() {// Return a new promise.return new Promise(function(resolve, reject) {let pdfCreation = false;var docDefinition = {pageSize: 'A4',pageMargins: [ 40, 60, 40, 60 ],content: ['First paragraph','Another paragraph, this time a little bit longer to make sure, this line will be divided into at least two lines']}const fontDescriptors = {Roboto: {normal: './fonts/Roboto-Regular.ttf',bold: './fonts/Roboto-Medium.ttf',italics: './fonts/Roboto-Italic.ttf',bolditalics: './fonts/Roboto-MediumItalic.ttf',}};const printer = new PdfPrinter(fontDescriptors);const pdfDoc = printer.createPdfKitDocument(docDefinition);const storage = new Storage();let file_name = uuidv4() + '.pdf';const myPdfFile = storage.bucket(BUCKET).file(file_name);pdfDoc.pipe(myPdfFile.createWriteStream()).on('finish', function (){console.log('Pdf successfully created!');resolve(file_name);}).on('error', function(err){console.log('Error during the wirtestream operation in the new file');reject('Error: something goes wrong ! '+ err);});pdfDoc.end();});}

the docDefinition json of the createPDF() function is the json in which we define all data and template of our pdf. This is a simple pdf but you can create complex layout following the rules described in the documentation of the library. This json in this example is wrapped in this function, but you can store it in cloud storage, you can also send it trough the request etc..

this is the full index.js file that you have to deploy on GCP

require('@google-cloud/debug-agent');
const Storage = require('@google-cloud/storage');
const fs = require('fs');
const request = require('request');
const PdfPrinter = require('pdfmake/src/printer');
const uuidv4 = require('uuid/v4');
const Google = require('googleapis');
const BUCKET = 'share-bucket'; // Replace with name of your bucket
/*------------------------------------------------------- verify if the access token is valid or not
- create pdf
- store pdf on bucket
--------------------------------------------------------*/function getAccessToken(header) {
if (header) {
var match = header.match(/^Bearer\s+([^\s]+)$/);
if (match) {
return match[1];
}
}
return null;
}
function createPDF() {// Return a new promise.return new Promise(function(resolve, reject) {let pdfCreation = false;var docDefinition = {pageSize: 'A4',pageMargins: [ 40, 60, 40, 60 ],content: ['First paragraph','Another paragraph, this time a little bit longer to make sure, this line will be divided into at least two lines']}const fontDescriptors = {Roboto: {normal: './fonts/Roboto-Regular.ttf',bold: './fonts/Roboto-Medium.ttf',italics: './fonts/Roboto-Italic.ttf',bolditalics: './fonts/Roboto-MediumItalic.ttf',}};const printer = new PdfPrinter(fontDescriptors);const pdfDoc = printer.createPdfKitDocument(docDefinition);const storage = new Storage();let file_name = uuidv4() + '.pdf';const myPdfFile = storage.bucket(BUCKET).file(file_name);pdfDoc.pipe(myPdfFile.createWriteStream()).on('finish', function (){console.log('Pdf successfully created!');resolve(file_name);}).on('error', function(err){console.log('Error during the wirtestream operation in the new file');reject('Error: something goes wrong ! '+ err);});pdfDoc.end();});}function authorized(res) {createPDF().then(function(file_name){res.status(200).send("The request was successfully authorized and pdf generated.\n You can find your pdf in the cloud storage " + file_name);}).catch( function(error) {console.error("Failed!" + error);res.status(400).send("Error: Pdf generation failed!");});}/**
* Cloud Function.
*
* @param {Object} req Cloud Function request context.
* @param {Object} res Cloud Function response context.
*/
exports.yourFunctionName = function yourFunctionName(req, res) {
console.log('this is the request');
console.log(req);
var accessToken = getAccessToken(req.get('Authorization'));
var oauth = new Google.auth.OAuth2();
oauth.setCredentials({access_token: accessToken});
var permission = 'storage.buckets.get';
var gcs = Google.storage('v1');
gcs.buckets.testIamPermissions(
{bucket: BUCKET, permissions: [permission], auth: oauth}, {},
function (err, response) {
if (response && response['permissions'] && response['permissions'].includes(permission)) {
authorized(res);
} else {
console.log('---Error below---');
console.log(err);
res.status(403).send("The request is forbidden.");
}
});
};

perform the gcloud command to deploy your function

$ gcloud beta functions deploy yourFunctionName --stage-bucket share-bucket --trigger-http

in the end call your function with a valid access token to test it and create the pdf

curl https://us-central1-yourprojectid.cloudfunctions.net/yourFunctionName -H "Authorization: Bearer ya29.dhasodhwasjndioasjdsadjasidjsadjioasdjiosajdiosajdiosadjsioaiojdasdjiosjasiodjiosadjiosajdiosjdiosajoadsjios"

Below some useful link and some tip

for this specific library, you have to create a folder in which are stored fonts used from the library to create the pdf, of course, you can choose other fonts and customize them as you want. Take a look at fontDescriptors object in the createPDF() function how to set them

In the following links is described how to secure a cloud function and an explanation about cloud function execution environment


End of the journey, some considerations

  • the free tier of GCP cf is very huge: you can perform 2 millions of calls per month to the function, thus “it’s almost free”
  • it’s a serverless approach so you have to care just to write the code of the function
  • a plethora of nodejs module that can be used

Thanks for reading!

If you need further information about the article or if you want to share something about this topic write to me or put some comment below.

Ivan Benegiamo

Written by

Don’t Be Bored. Do Something. Unlike the ordinary!

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade