Skip to content

Instantly share code, notes, and snippets.

@revmischa
Created March 24, 2022 13:31
Show Gist options
  • Save revmischa/0a4238464c7c905ca19b901dcc8b96c5 to your computer and use it in GitHub Desktop.
Save revmischa/0a4238464c7c905ca19b901dcc8b96c5 to your computer and use it in GitHub Desktop.
Prisma CDK lambda layer
import { Construct } from "constructs"
import { Code, LayerVersion, LayerVersionProps } from "aws-cdk-lib/aws-lambda"
import { AssetHashType, IgnoreMode } from "aws-cdk-lib"
import crypto from "crypto"
import path from "path"
import { getProjectRoot } from "./paths"
import { DEFAULT_RUNTIME } from "../stacks"
// deps to npm install to the layer
const PRISMA_DEPS = ["prisma", "@prisma/migrate", "@prisma/sdk", "@prisma/client"]
export const PRISMA_LAYER_EXTERNAL = [
"aws-sdk",
"@prisma/migrate",
"@prisma/sdk",
"@prisma/engines",
"@prisma/engines-version",
]
export interface PrismaLayerProps extends Omit<LayerVersionProps, "code"> {
// e.g. 3.7.0
prismaVersion?: string
// some more modules to add to the layer
nodeModules?: string[]
// use a pre-built layer zip file
layerZipPath?: string
}
/**
* Construct a lambda layer with Prisma libraries.
* Be sure to omit the prisma layer modules from your function bundles with the `externalModules` option.
* Include `environment` to point prisma at the right library location.
*
* @example
* ```ts
* const prismaLayer = new PrismaLayer(this, "PrismaLayer", {
* layerVersionName: `${id}-prisma`,
* removalPolicy: isProduction ? RemovalPolicy.RETAIN : RemovalPolicy.DESTROY,
* })
*
* // default lambda function options
* const functionOptions: FunctionOptions = {
* layers: [prismaLayer],
* environment: { ...prismaLayer.environment, DEBUG: "*" },
* bundling: {
* externalModules: prismaLayer.externalModules,
* },
* }
*/
export class PrismaLayer extends LayerVersion {
externalModules: string[]
environment: Record<string, string>
constructor(scope: Construct, id: string, props: PrismaLayerProps = {}) {
const { prismaVersion, layerZipPath } = props
const nodeModules = props.nodeModules || []
const layerDir = "/asset-output/nodejs"
const nm = `${layerDir}/node_modules`
const engineDir = `${nm}/@prisma/engines`
const sdkDir = `${nm}/@prisma/sdk`
// what are we asking npm to install?
let modulesToInstall = PRISMA_DEPS
if (prismaVersion) modulesToInstall = modulesToInstall.map((dep) => `${dep}@${prismaVersion}`)
const modulesToInstallArgs = modulesToInstall.concat(nodeModules).join(" ")
const createBundleCommand = [
// create asset bundle in docker
"bash",
"-c",
[
`mkdir -p ${layerDir}`,
// install PRISMA_DEPS
`cd ${layerDir} && HOME=/tmp npm install ${modulesToInstallArgs}`,
// delete unneeded engines
`rm -f ${engineDir}/introspection-engine* ${engineDir}/prisma-fmt*`,
// sdk sux
`rm -f ${sdkDir}/dist/libquery_engine*`,
`rm -rf ${sdkDir}/dist/get-generators/*engine*`,
// get rid of some junk
`rm -rf ${nm}/prisma/build/public`,
`rm -rf ${nm}/prisma/prisma-client/src/__tests__`,
`rm -rf ${nm}/@types`,
].join(" && "),
]
// hash our parameters so we know when we need to rebuild
const bundleCommandHash = crypto.createHash("sha256")
bundleCommandHash.update(JSON.stringify(createBundleCommand))
// bundle
let code: Code
if (layerZipPath) {
code = Code.fromAsset(path.join(getProjectRoot(), layerZipPath))
} else {
code = Code.fromAsset(".", {
// don't send all our files to docker (slow)
ignoreMode: IgnoreMode.GLOB,
exclude: ["*"],
// if our bundle commands (basically our "dockerfile") changes then rebuild the image
assetHashType: AssetHashType.CUSTOM,
assetHash: bundleCommandHash.digest("hex"),
bundling: {
image: DEFAULT_RUNTIME.bundlingImage,
command: createBundleCommand,
},
})
}
super(scope, id, { ...props, code })
// hint for prisma to find the engine
this.environment = {
PRISMA_QUERY_ENGINE_LIBRARY:
"/opt/nodejs/node_modules/@prisma/engines/libquery_engine-rhel-openssl-1.0.x.so.node",
}
// modules provided by layer
this.externalModules = [...PRISMA_LAYER_EXTERNAL, ...nodeModules]
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment