prisma: Serverless Framework project fails to deploy because of archive size

Bug description

Generated bundle of the Serverless project has too big size and exceeds allowed by AWS Lambda 250 MB. It happens because of big sizes of downloaded @prisma/client, which, for some reasons, downloads engines folder that has unzipped size of 96 MB. And the other big files are connected to generated using command prisma generate schema. image image image

Dependency versions

@prisma/client: “^2.20.0” prisma (dev) : “^2.20.1” serverless (dev): “^2.32.1” serverless-webpack: “^5.4.0” webpack: “^5.11.0” @types/webpack: “^4.41.25”

Webpack config

import nodeExternals from 'webpack-node-externals';
import slsw from 'serverless-webpack';
import { TsconfigPathsPlugin } from 'tsconfig-paths-webpack-plugin';
import CopyPlugin from 'copy-webpack-plugin';

const isOffline = !!slsw.lib.webpack.isLocal;

if (isOffline) {
    // eslint-disable-next-line no-console
    console.log('Offline mode detected!');
}

// Add plugins for the offline mode here
const offlinePlugins: NonNullable<Configuration['plugins']> = [];

const config: Configuration = {
    target: 'node',
    entry: slsw.lib.entries,
    externals: [nodeExternals()],
    mode: isOffline ? 'development' : 'production',
    devtool: 'source-map',
    module: {
        rules: [
            {
                test: /\.ts$/,
                use: [
                    'source-map-loader',
                    {
                        loader: 'ts-loader',
                        options: {
                            onlyCompileBundledFiles: true,
                        },
                    },
                ],
            },
            {
                test: /\.graphql$/,
                use: ['raw-loader'],
            },
        ],
    },
    plugins: [
        ...(isOffline ? offlinePlugins : []),
        new CopyPlugin({
            patterns: [
                { from: './src/database/prisma/schema.prisma', to: './schema.prisma' },
            ],
        })],
    resolve: {
        plugins: [
            new TsconfigPathsPlugin(),
        ],
        extensions: ['.ts', '.js'],
    },
    optimization: {
        minimize: !isOffline,
    },
    stats: 'minimal',
};

export = config;

Serverless webpack options

    webpackConfig: webpack.config.ts
    includeModules: true
    packager: npm
    packagerOptions:
      scripts:
        - prisma generate

Environment

& setup

  • OS: Mac OS Big Sur 11.1
  • Database: PostgreSQL
  • Node.js version: 12
  • Prisma version: 2.20.1

Background

This is on the first deploy of Prisma to our existing Lambda stack. We have not been able to get Lambda working yet. We bundle our functions through webpack before deploying.

I have looked at the webpack bundle and confirm that @prisma/client is being bundled with the lambda function before deploying.

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Reactions: 7
  • Comments: 60 (31 by maintainers)

Commits related to this issue

Most upvoted comments

@janpio I created a minimum repro at https://github.com/jsw/prisma-issue-6644

Also, I fixed part of the issue by moving the packages.patterns from function scope to file scope, though both are supposed to work if I’m correctly following the serverless docs. Take a look at my serverless.yml. And @prisma/engines and prisma are still included unless your workaround above is applied.

Quick update: I can confirm that we upload both binaries, the native and the rhel one, in standard Serverless Framework projects. No Webpack required even. If your own application code is big, this can of course lead to upload (50MB limit) or unpack (150MB limit) problems. I’ll look for a solution and let you know again.

As my development environment is Windows 10. After using @jsw solution, my package size is still 62MB. I have updated my serverless package patterns to the following, and now my package size is 19MB.

- '!node_modules/.prisma/client/libquery_engine-*'
- 'node_modules/.prisma/client/libquery_engine-rhel-*'
- '!node_modules/prisma/libquery_engine-*'
- '!node_modules/@prisma/engines/**'

# EXCLUDE query_engine-windows.dll.node
- '!node_modules/.prisma/client/query_engine-*'
# EXCLUDE .cache folder
- '!node_modules/.cache/**'
# EXCLUDE .bin folder
- '!node_modules/.bin/**'

For those using serverless platform, since upgrading to prisma 3, I have updated my serverless package patterns to the following, which seems to cover everything

package:
  patterns:
    - '!node_modules/@prisma/engines/**'
    - '!node_modules/.prisma/client/libquery_engine-*'
    - 'node_modules/.prisma/client/libquery_engine-rhel-*'
    - '!node_modules/prisma/libquery_engine-*'
    - 'node_modules/prisma/libquery_engine-rhel-*'

The prisma documentation seems incomplete.

https://www.prisma.io/docs/guides/deployment/deployment-guides/deploying-to-aws-lambda#package-pattern-in-serverlessyml

says to use:

package:
  patterns:
    - '!node_modules/.prisma/client/libquery_engine-*'
    - 'node_modules/.prisma/client/libquery_engine-rhel-*'

https://www.prisma.io/docs/guides/deployment/deployment-guides/caveats-when-deploying-to-aws-platforms#deleting-prisma-engines-that-are-not-required

says that node_modules/@prisma/engines can be excluded.

And finally, as mentioned in an earlier comment, there are engine libraries in node_modules/prisma that can be excluded.

cc @janpio

@AndriiSherman That is weird, as you are explicitly asking Serverless Framework to exclude that path, right? Can you please open a new issue? Then we can debug this in detail.

@allanitis The explicit excludeFiles is ignored, or are there other engines in the archive? Optimally also create a new issue and we can debug this individually.

@NikitaHarzha In your project I am observing Serverless Framewor including all devDependencies, even when Prisma and Prisma Client are not installed. Can you confirm this behavior? The bundled file via sls package should in theory only include dependencies and their dependencies in the node_modules folder.


I will come up with a more general package.patterns entry that should include all files from Prisma CLI and Prisma Client later, to work around Serverless Framework somehow, sometimes not excluding devDependencies. But this will unfortunately only help for Prisma itself. I fear you are only noticing the Prisma inclusion because of the file size (which of course makes it most pressing and relevant), but it looks to me that Serverless Framework is failing to exclude unneeded dependencies in general somehow.

Thanks, I opened a PR from your patch at https://github.com/prisma/docs/pull/5726

As written before, we have reduced the size of our engines a lot and as far as we know, this solved this problem for most our user and their use cases. Hence I am closing this issue for now.

If you and your project are still having problems because of the remaining size of our engines, please open a new issue and describe your situation - we are happy to take a look and hopefully fix that. Thanks!

In the case of serverless-webpack, dependencies are installed in a separate directory and files are combined during packaging.

If you use the plugin below, it automatically performs prisma generate after installing serverless-webpack dependency and automatically deletes unnecessary engines.

The @prisma/engines and prisma dependencies should not be included in the first place at all. We will need to investigate why that is happening. Can you maybe share your project or a minimal reproduction repository of the relevant bits?

I guess the following additional patterns should fix it as a workaround:

        - '!node_modules/@prisma/engines/query-engine-*'
        - '!node_modules/prisma/query-engine-*'

@Jolg42 Thanks so much for looking into this. I opened new case #7016 as suggested. I am not sure how I can control node_modules/@prisma/engines as I only have the following prisma dependencies in packages.json (showing the currently working case). When upgrading those two packages to > “2.19.0” I run into the AWS lambda size limits on Vercel.

  "dependencies": {
   ...
    "@prisma/client": "2.19.0",
  },
  "devDependencies": {
    ...
    "prisma": "2.19.0"
  }

There is more detailed info on the new case.

If this helps shed any light on the issue, I’m having size issues because serverless will not remove the binaries in node_modules/.prisma/client via:

 patterns:
    - "!node_modules/.prisma/client/query-engine-*"
    - "node_modules/.prisma/client/query-engine-rhel-*"

@janpio sure, no problems!

Ah yes, I have that of course, I copy pasted your configuration from above - that is what is confusing me.

But I’ll continue the investigation into the size first before trying to fix this now.

@janpio Resources file just contains CORS settings

Resources file

Resources:
  GatewayResponseDefault4XX:
    Type: 'AWS::ApiGateway::GatewayResponse'
    Properties:
      ResponseParameters:
        gatewayresponse.header.Access-Control-Allow-Origin: "'*'"
        gatewayresponse.header.Access-Control-Allow-Headers: "'*'"
      ResponseType: DEFAULT_4XX
      RestApiId:
        Ref: 'ApiGatewayRestApi'

src/lambdas/test.handler is an exact path to the lambda in my project Lambda function just has test of the setup of Prisma, just one test request

Test lambda function

import { apiGatewayResponse } from '@/middlewares/apiGatewayResponse';
import { APIGatewayEvent, DefaultEvent, DefaultResponse } from '@/types/aws';
import { APIGatewayResponse } from '@/utils/aws';
import middy from 'middy';
import { v4 as uuidv4 } from 'uuid';
import { jsonBodyParser } from 'middy/middlewares';
import { PrismaClient } from '@prisma/client';

const prisma = new PrismaClient();

const rawHandler = async (
    event: APIGatewayEvent<DefaultEvent>,
): Promise<APIGatewayResponse<DefaultResponse>> => {
    const newUser = await prisma.user.create({
        data: {
            id: uuidv4(),
            name: 'Top',
            email: 'nikita@labda.direct',
            role: 'FREELANCER',
            createdAt: new Date(),
        },
    });

    console.log('New user', newUser);

    return { message: 'Success' };
};

export const handler = middy(rawHandler)
    .use(jsonBodyParser())
    .use(apiGatewayResponse<APIGatewayEvent<DefaultEvent>,
    APIGatewayResponse<DefaultResponse>>());

But it has my custom types for the event and response, you will need to use similar types from aws-sdk to make lambda work as expected

Oooookay, seems you are bundling the CLI as well here - I overlooked that before. Sorry.

Where do these files live? Do you need the CLI in the built project? Otherwise, if you put the prisma dependency into the devDependencies of your project this would automatically be excluded from the package completely. Update: You already have what I describe here. I’ll need to figure out what is going on here.

@janpio Thanks for your help! Unfortunately, it didn’t worked for me. Files are still included in a package and package size is too big. I tried to implement this in different ways, but still I am not able to deploy my changes image

@janpio Here is my serverless.yaml file

service: prisma-test
useDotenv: true
resources: ${file(resources.yaml)}

plugins:
  - serverless-offline
  - serverless-iam-roles-per-function
  - serverless-webpack

provider:
  name: aws
  runtime: nodejs12.x
  stage: ${opt:stage, 'dev'}
  region: ${opt:region, 'us-east-1'}
  memorySize: 512
  timeout: 30
  versionFunctions: false
  apiGateway:
    shouldStartNameWithService: true
  variableSyntax: "\\${((?!AWS|SecretName)[ ~:a-zA-Z0-9._@'\",\\-\\/\\(\\)\\*\\?]+?)}"
  environment:
    DB_USERNAME: ${self:custom.db.username}
    DB_PASSWORD: ${self:custom.db.password}
    DATABASE_URL: ${self:custom.db.dbUrl}
functions:
  TestFunction:
    handler: src/lambdas/test.handler
    name: ${self:custom.servicePrefix}-test
    events:
      - http:
          path: test
          method: get
          cors: true
      
custom:
  webpack:
    webpackConfig: webpack.config.ts
    includeModules: true
    packager: npm
    packagerOptions:
      scripts:
        - prisma generate
  servicePrefix: ${self:service}-${self:provider.stage}
  db:
    username: ${env:DB_USERNAME}
    password: ${env:DB_PASSWORD}
    dbUrl: ${env:DATABASE_URL}

Project dependencies and scripts

{
  "name": "sls",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "sls": "sls -c ./serverless.yaml",
    "build": "npm run sls package",
    "deploy": "npm run sls deploy -- -v",
    "undeploy": "npm run sls remove",
    "start": "npm run sls offline start -- --skipCacheInvalidation",
    "prisma:generate": "prisma generate",
    "prisma:migrate": "prisma  migrate dev --name  init  --preview-feature",
    "tslint": "eslint --ext .ts ./src"
  },
  "prisma": {
    "schema": "./src/database/prisma/schema.prisma"
  },
  "husky": {
    "hooks": {
      "pre-commit": "npm run tslint"
    }
  },
  "author": "LambdaTeam",
  "license": "ISC",
  "devDependencies": {
    "@types/copy-webpack-plugin": "^6.4.1",
    "@types/node": "^14.14.37",
    "@types/uuid": "^8.3.0",
    "@types/webpack": "^4.41.25",
    "@types/webpack-node-externals": "^2.5.1",
    "@typescript-eslint/eslint-plugin": "^4.20.0",
    "aws-sdk": "^2.877.0",
    "copy-webpack-plugin": "^8.1.1",
    "eslint": "^7.23.0",
    "eslint-config-airbnb-typescript": "^12.3.1",
    "eslint-plugin-import": "^2.22.1",
    "husky": "^6.0.0",
    "prisma": "^2.20.1",
    "serverless": "^2.32.1",
    "serverless-iam-roles-per-function": "^3.1.0",
    "serverless-offline": "^6.9.0",
    "serverless-webpack": "^5.4.0",
    "source-map-loader": "^2.0.1",
    "ts-loader": "^8.1.0",
    "ts-node": "^9.1.1",
    "tsconfig-paths-webpack-plugin": "^3.5.1",
    "typescript": "^4.2.3",
    "webpack": "^5.11.0",
    "webpack-node-externals": "^2.5.2"
  },
  "dependencies": {
    "@hapi/boom": "^9.1.2",
    "@prisma/client": "^2.20.1",
    "middy": "^0.36.0",
    "uuid": "^8.3.2"
  }
}

Prisma config

generator client {
 provider      = "prisma-client-js"
 output        = "./src/database/client"
 binaryTargets = ["native", "rhel-openssl-1.0.x"]
}