Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I package symbolic link in node_modules with `serverless?

I am using serverless to package nodejs application. I am using yarn workspace in my project.

- common
- projectA
- projectB

the projectA and projectB are using common module which is managed by yarn workspace. It creates a link inside node_modules/common -> ../common. But when I package the application with sls deploy, it doesn't inlude the link node_modules/common. How can I make it package symbolic link?

like image 551
Joey Yi Zhao Avatar asked Oct 26 '25 06:10

Joey Yi Zhao


1 Answers

You should start using code bundler.

What is code bundler?

What code bundler does:

  1. It scans your AWS Lambda code structure through all of the files, starting from handler file.
  2. It goes through all of the imports to create a tree of dependencies.
  3. Then it inlines all of those dependencies into single "fat" file.
  4. After that you are free to deploy your application, which has only single file.

As you can see, it's a perfect match for AWS Lambda and your use case.

All of the dependencies from common package will be included in the output file.

Also code bundlers have other cool features, like removing all of the unneeded files, that are defined in libraries that you use, but you are not using them directly. Due to this output package size of your Lambda will be a lot smaller, which will decrease cold starts.

How to achieve that using Serveless Framework

The easiest way is to start with serverless-webpack plugin, which includes Webpack (one of the most popular code bundlers) and some most common configurations for it.

After adding this plugin, simply configure it in serverless.yml:

custom:
  webpack:
    webpackConfig: 'webpack.config.js'   # you can remove it, it's the same as default
    packager: 'yarn'

Now you need to configure Webpack using webpack.config.js file. There are a lot of possibilities to configure it and the example below is the most basic one:

const path = require('path');
const slsw = require('serverless-webpack');

module.exports = {
  entry: slsw.lib.entries,
  target: 'node',
  mode: slsw.lib.webpack.isLocal ? 'development' : 'production',
  stats: 'minimal',
  devtool: 'nosources-source-map',
  externals: [{'aws-sdk': 'commonjs aws-sdk'}],
  resolve: {
    extensions: ['.js', '.json'],
  },
  output: {
    libraryTarget: 'commonjs2',
    path: path.join(__dirname, '.webpack'),
    filename: '[name].js',
    sourceMapFilename: '[file].map',
  },
};

Now when you call sls package in projectA or projectB, then after unzipping ./.serverless/functionName.zip, you will find just single "fat" file, that will include all of the required dependencies.

During sls deploy phase, this file will be deployed as Lambda handler.

Correctly defining dependencies

Make sure, that common package is listed as dependency of projectA and projectB:

// common/package.json

{
  "name": "@your-project/common",
  "version": "1.0.0",
  "license": "ISC",
}

// projectA/package.json

{
  "name": "@your-project/packageA",
  "version": "1.0.0",
  "license": "ISC",
  "dependencies": {
      "@your-project/common": "1.0.0"
  }
}

Thanks to this, you will be able to reference commons in pakcageA imports via:

import exampleHelper from '@your-project/common/src/exampleHelper';

Project using this approach can be found on my Github here:

https://github.com/PatrykMilewski/serverless-series

like image 102
PatrykMilewski Avatar answered Oct 29 '25 19:10

PatrykMilewski



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!