Create PDF using PdfKit on Serverless AWS Lambda with Layer
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
*binaryMediaTypes. It will add application/pdf as Binary Media Types in API Gateway settings.
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 theAccept
header of the original request match an entry of thebinaryMediaTypes
list, API Gateway passes through the body. This occurs when theContent-Type
header and theAccept
header are the same; otherwise, API Gateway converts the response body to the type specified in theAccept
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.
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.
Here is the full code https://github.com/crespowang/serverless-lambda-pdfkit