Deploy to AWS Lambda
This guide explains how to avoid common issues when deploying a project using Prisma ORM to AWS Lambda.
While a deployment framework is not required to deploy to AWS Lambda, this guide covers deploying with:
- AWS Serverless Application Model (SAM) is an open-source framework from AWS that can be used in the creation of serverless applications. AWS SAM includes the AWS SAM CLI, which you can use to build, test, and deploy your application.
- Serverless Framework provides a CLI that helps with workflow automation and AWS resource provisioning. While Prisma ORM works well with the Serverless Framework "out of the box", there are a few improvements that can be made within your project to ensure a smooth deployment and performance. There is also additional configuration that is needed if you are using the
serverless-webpack
orserverless-bundle
libraries. - SST provides tools that make it easy for developers to define, test, debug, and deploy their applications. Prisma ORM works well with SST but must be configured so that your schema is correctly packaged by SST.
General considerations when deploying to AWS Lambda
This section covers changes you will need to make to your application, regardless of framework. After following these steps, follow the steps for your framework.
Define binary targets in Prisma Schema
Depending on the version of Node.js, your Prisma schema should contain either rhel-openssl-1.0.x
or rhel-openssl-3.0.x
in the generator
block:
- Node.js 16 and 18
- Node.js 20+
binaryTargets = ["native", "rhel-openssl-1.0.x"]
binaryTargets = ["native", "rhel-openssl-3.0.x"]
This is necessary because the runtimes used in development and deployment differ. Add the binaryTarget
to make the compatible Prisma ORM engine file available.
Lambda functions with arm64 architectures
Lambda functions that use arm64 architectures (AWS Graviton2 processor) must use an arm64
precompiled engine file.
In the generator
block of your schema.prisma
file, add the following:
binaryTargets = ["native", "linux-arm64-openssl-1.0.x"]
Prisma CLI binary targets
While we do not recommend running migrations within AWS Lambda, some applications will require it. In these cases, you can use the PRISMA_CLI_BINARY_TARGETS environment variable to make sure that Prisma CLI commands, including prisma migrate
, have access to the correct schema engine.
In the case of AWS lambda, you will have to add the following environment variable:
PRISMA_CLI_BINARY_TARGETS=native,rhel-openssl-1.0.x
prisma migrate
is a command in the prisma
package. Normally, this package is installed as a dev dependency. Depending on your setup, you may need to install this package as a dependency instead so that it is included in the bundle or archive that is uploaded to Lambda and executed.
Connection pooling
Generally, when you use a Function as a Service (FaaS) environment to interact with a database, every function invocation can result in a new connection to the database. This is not a problem with a constantly running Node.js server. Therefore, it is beneficial to pool database connections to get better performance. You can use Accelerate to solve this issue. For other solutions, see the connection management guide for serverless environments.
Deploying with AWS SAM
Loading environment variables
AWS SAM does not directly support loading values from a .env
file. You will have to use one of AWS's services to store and retrieve these parameters. This guide provides a great overview of your options and how to store and retrieve values in Parameters, SSM, Secrets Manager, and more.
Loading required files
AWS SAM uses esbuild to bundle your TypeScript code. However, the full esbuild API is not exposed and esbuild plugins are not supported. This leads to problems when using Prisma ORM in your application as certain files (like schema.prisma
) must be available at runtime.
To get around this, you need to directly reference the needed files in your code to bundle them correctly. In your application, you could add the following lines to your application where Prisma ORM is instantiated.
import schema from './prisma/schema.prisma'
import x from './node_modules/.prisma/client/libquery_engine-rhel-openssl-1.0.x.so.node'
if (process.env.NODE_ENV !== 'production') {
console.debug(schema, x)
}
You will also need to define how to bundle these files with esbuild by adding the following lines to Metadata.BuildProperties
in your template.yaml
:
Loader:
- .prisma=file
- .so.node=file
AssetNames: '[name]'
This will make sure that files needed by Prisma ORM will be included in the AWS SAM build.
Deploying with the Serverless Framework
Loading environment variables via a .env
file
Your functions will need the DATABASE_URL
environment variable to access the database. The serverless-dotenv-plugin
will allow you to use your .env
file in your deployments.
First, make sure that the plugin is installed:
npm install -D serverless-dotenv-plugin
Then, add serverless-dotenv-plugin
to your list of plugins in serverless.yml
:
plugins:
- serverless-dotenv-plugin
The environment variables in your .env
file will now be automatically loaded on package or deployment.
serverless package
Deploy only the required files
To reduce your deployment footprint, you can update your deployment process to only upload the files your application needs. The Serverless configuration file, serverless.yml
, below shows a package
pattern that includes only the Prisma ORM engine file relevant to the Lambda runtime and excludes the others. This means that when Serverless Framework packages your app for upload, it includes only one engine file. This ensures the packaged archive is as small as possible.
package:
patterns:
- '!node_modules/.prisma/client/libquery_engine-*'
- 'node_modules/.prisma/client/libquery_engine-rhel-*'
- '!node_modules/prisma/libquery_engine-*'
- '!node_modules/@prisma/engines/**'
- '!node_modules/.cache/prisma/**' # only required for Windows
If you are deploying to Lambda functions with ARM64 architecture you should update the Serverless configuration file to package the arm64
engine file, as follows:
package:
patterns:
- '!node_modules/.prisma/client/libquery_engine-*'
- 'node_modules/.prisma/client/libquery_engine-linux-arm64-*'
- '!node_modules/prisma/libquery_engine-*'
- '!node_modules/@prisma/engines/**'
If you use serverless-webpack
, see Deployment with serverless webpack below.
Deployment with serverless-webpack
If you use serverless-webpack
, you will need additional configuration so that your schema.prisma
is properly bundled. You will need to:
- Copy your
schema.prisma
withcopy-webpack-plugin
. - Run
prisma generate
viacustom > webpack > packagerOptions > scripts
in yourserverless.yml
. - Only package the correct Prisma ORM engine file to save more than 40mb of capacity.
1. Install webpack specific dependencies
First, ensure the following webpack dependencies are installed:
npm install --save-dev webpack webpack-node-externals copy-webpack-plugin serverless-webpack
2. Update webpack.config.js
In your webpack.config.js
, make sure that you set externals
to nodeExternals()
like the following:
const nodeExternals = require('webpack-node-externals')
module.exports = {
// ... other configuration
externals: [nodeExternals()],
// ... other configuration
}
Update the plugins
property in your webpack.config.js
file to include the copy-webpack-plugin
:
const nodeExternals = require('webpack-node-externals')
const CopyPlugin = require('copy-webpack-plugin')
module.exports = {
// ... other configuration
externals: [nodeExternals()],
plugins: [
new CopyPlugin({
patterns: [
{ from: './node_modules/.prisma/client/schema.prisma', to: './' }, // you may need to change `to` here.
],
}),
],
// ... other configuration
}
This plugin will allow you to copy your schema.prisma
file into your bundled code. Prisma ORM requires that your schema.prisma
be present in order make sure that queries are encoded and decoded according to your schema. In most cases, bundlers will not include this file by default and will cause your application to fail to run.
Depending on how your application is bundled, you may need to copy the schema to a location other than ./
. Use the serverless package
command to package your code locally so you can review where your schema should be put.
Refer to the Serverless Webpack documentation for additional configuration.
3. Update serverless.yml
In your serverless.yml
file, make sure that the custom > webpack
block has prisma generate
under packagerOptions > scripts
as follows:
custom:
webpack:
packagerOptions:
scripts:
- prisma generate
This will ensure that, after webpack bundles your code, the Prisma Client is generated according to your schema. Without this step, your app will fail to run.
Lastly, you will want to exclude Prisma ORM query engines that do not match the AWS Lambda runtime. Update your serverless.yml
by adding the following script that makes sure only the required query engine, rhel-openssl-1.0.x
, is included in the final packaged archive.
custom:
webpack:
packagerOptions:
scripts:
- prisma generate
-- find . -name "libquery_engine-*" -not -name "libquery_engine-rhel-openssl-*" | xargs rm
If you are deploying to Lambda functions with ARM64 architecture you should update the find
command to the following:
custom:
webpack:
packagerOptions:
scripts:
- prisma generate
-- find . -name "libquery_engine-*" -not -name "libquery_engine-arm64-openssl-*" | xargs rm
4. Wrapping up
You can now re-package and re-deploy your application. To do so, run serverless deploy
. Webpack output will show the schema being moved with copy-webpack-plugin
:
serverless package
Deploying with SST
Working with environment variables
While SST supports .env
files, it is not recommended. SST recommends using Config
to access these environment variables in a secure way.
The SST guide available here is a step-by-step guide to get started with Config
. Assuming you have created a new secret called DATABASE_URL
and have bound that secret to your app, you can set up PrismaClient
with the following:
import { PrismaClient } from '@prisma/client'
import { Config } from 'sst/node/config'
const globalForPrisma = global as unknown as { prisma: PrismaClient }
export const prisma =
globalForPrisma.prisma ||
new PrismaClient({
datasourceUrl: Config.DATABASE_URL,
})
if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma
export default prisma