Create PDF using PdfKit on Serverless AWS Lambda with Layer

Crespo Wang
The Startup
Published in
4 min readFeb 14, 2020

--

Pdfkit is a popular tool for manipulating PDF, and running it on AWS Lambda makes it even more powerful because of the benefits from being serverless, but it can be a bit tricky to set up from the ground.

As always I use Serverless framework to manage the AWS resources, and I use serverless-offline to simulate local dev environment. The AWS Lambda runtime is nodejs12.x, and the code is written in TypeScript.

> Serverless

Serverless.yml

*binaryMediaTypes. It will add application/pdf as Binary Media Types in API Gateway settings.

API Gateway

This is very important because we don’t want API Gateway to convert the response but only just pass-through.

If the Content-Type header of the response and the Accept header of the original request match an entry of thebinaryMediaTypes list, API Gateway passes through the body. This occurs when the Content-Type header and the Acceptheader are the same; otherwise, API Gateway converts the response body to the type specified in the Accept header

> The code

Async/Await is a very sexy feature added in ES2017 (ES8), but pdfkit hasn’t added the support for it, but we can wrap the PDF generation code into an async function.

We want to return the body as base64 encoded, so make sure the stream is converted to base64 and isBase64Encoded is true.

> Layer

When deploying the lambda function, serverless-webpack will pack the code and its node module dependencies into a zip file and upload to AWS.

You can see what are in the package

yarn sls package
ls -lah .serverless
unzip serverless-lambda-pdfkit.zip -d unzipped
du -sh unzipped/node_modules/* | sort -h

You can see that pdfkit and its dependencies are pretty big.

One of the factors that slow down AWS Lambda code start is the function size, the smaller the faster the function starts. So we should reduce the size when possible, using Layer is an ideal way for it.

There is a certain directory structure you must follow when making a layer file, for nodejs runtime, the unzipped layer must live under a folder named nodejs. So first we create a directory and name it nodejs.

mkdir -p nodejs
cd nodejs

Then we install pdfkit here in order to download all needed node modules

npm init
npm install pdfkit

It will create the package.json and install all node modules under the nodejs folder.

package.json

The last step is to zip the nodejs directory into a file

zip -9r pdfkit-layer.zip nodejs

Now we have the layer zip file ready, we just need to tell serverless.yml to use it.

And attach the layer to the function. You must follow the naming convention to reference a layer, that is

To use a layer with a function in the same service, use a CloudFormation Ref. The name of your layer in the CloudFormation template will be your layer name TitleCased (without spaces) and have LambdaLayer appended to the end.

In this case, the layer name we defined is Pdfkit, so the reference name must be PdfkitLambdaLayer

Now that we have the layer attached to the function, we can tell webpack to exclude pdfkit from the deployment package.

Now the package size has dropped from 9.5MB to 153KB!

> Deploy and run

Simply run yarn sls deploy will deploy the function to your AWS Lambda

You can test it by sending a GET request with Accept: application/pdf to the API endpoint.

--

--

Crespo Wang
The Startup

I stir fry JS, steam AWS and code Beijing duck