Search code examples
firebaseaws-lambdaangularfire2serverless-frameworkaws-lambda-layers

AWS Lambda, adding firebase module and its dependencies to function for use with Angular Universal


I have an Angular Universal project deploying to AWS Lambda with SSR but I haven't managed to get firebase to work as a dependency with it. All the errors in the console are related to the firebase dependency & its dependencies.

Method #1: Include firebase module

package:
  exclude:
    - '!node_modules/firebase/**'

This adds firebase and now the errors moves to being unable to find dependencies of firebase and so on.

Method #2: Just include all node modules

This is an Angular project and I have 600mb+ of dependencies that build the app,

Method #3: Installing firebase inside my Angular 'dist' folder.

This will throw a runtime error:

Error: Failed to load gRPC binary module because it was not installed for the current system\nExpected directory: node-v64-linux-x64-glibc\nFound: [node-v64-win32-x64-unknown]\nThis problem can often be fixed by running \"npm rebuild\" on the current system\nOriginal error: Cannot find module '/var/task/node_modules/grpc/src/node/extension_binary/node-v64-linux-x64-glibc/grpc_node.node'",

Method #4: serverless-webpack

This will throw error during sls deploy saying it can't find webpack config, but it's right there in the root directory next to the serverlesss.yml and it's already used by angular universal, I can't overwrite it with different configs for serverless-webpack plugin.

Method #5: Manual Lambda Layer

I have tried making a layer with a package.json that has firebase as a dependency, ran npm install, made an archive of the nodejs folder that has the node_modules folder and uploaded it as a layer + manually added to my function.

Same error as #1, can't find the firebase module, this would also be overwritten by the serverless framework on my next deploy.

Method #6: Lambda layer via serverless

I've tried to piece these articles together to get something working Article 1, Article 2, Article 3 and used the example from this github but switched moment with firebase.

But none of them worked, the serverless config:

layers:
  firebaseLayer:
    path: ../layers/firebase-layer
    compatibleRuntimes:
      - nodejs8.10
      - nodejs10.x
    package:
      include:
        - node_modules/**

Would throw: No file matches include / exclude patterns

I also have const firebase = require('firebase'); inside my endpoint handler.

I've been trying to get this working for two whole days and it has been beyond frustrating. Any help is appreciated.


Solution

  • Two possible solutions:

    1. Use serverless-webpack with the following configuration:
    # serverless.yaml
    service: serverless-webpack-firebase
    
    provider:
      name: aws
      runtime: nodejs10.x
      stage: ${opt:stage, 'dev'}
      region: ${opt:region, 'us-east-1'}
    
    plugins:
      - serverless-webpack
    
    package:
      individually: true
    
    custom:
      webpack:
        webpackConfig: "webpack.config.js"
        includeModules: true
        packager: "yarn"
    
    functions:
      withWebpack:
        handler: handler.hello
    
    // webpack.config.js
    
    const path = require("path");
    const slsw = require("serverless-webpack");
    const nodeExternals = require("webpack-node-externals");
    
    module.exports = {
      entry: slsw.lib.entries,
      target: "node",
      mode: slsw.lib.webpack.isLocal ? "development" : "production",
      performance: {
        hints: false
      },
      resolve: {
        extensions: [".js", ".json"]
      },
      externals: [nodeExternals()]
    };
    

    externals: [nodeExternals()] will remove all external dependencies from the bundle and includeModules: true will still add them to the zip under node_modules

    1. Use a Lambda layer:
    # layer yaml
    service: firebase-layer
    
    provider:
      name: aws
      runtime: nodejs8.10
      stage: ${opt:stage, 'dev'}
      region: ${opt:region, 'us-east-1'}
    
    layers:
      firebase:
        path: ./layer
        description: Layer with all the required dependencies to use firebase
        compatibleRuntimes:
          - nodejs8.10
          - nodejs10.x
        licenseInfo: MIT
        retain: true
    
    

    and

    service: serverless-layer-firebase
    
    provider:
      name: aws
      runtime: nodejs10.x
      stage: ${opt:stage, 'dev'}
      region: ${opt:region, 'us-east-1'}
    
    plugins:
      - serverless-webpack
      - serverless-pseudo-parameters
    
    package:
      individually: true
    
    custom:
      webpack:
        webpackConfig: "webpack.config.js"
        includeModules: false
        packager: "yarn"
    
    functions:
      withLayer:
        handler: handler.hello
    
        layers:
          - arn:aws:lambda:${self:provider.region}:#{AWS::AccountId}:layer:firebase:1
    

    Same webpack.config.js, but this time with includeModules: false as we don't need the dependencies in the zip file (they are loaded with the layer).

    Layer structure:

    enter image description here

    Repository is available here: https://github.com/erezrokah/serverless-webpack-firebase

    See the differences in package size:

    enter image description here