Amplify: how to share code without Lambda Layers or private NPM

Presently sponsored by Serverless Guru: Your guide to cloud excellence, helping you every step of your serverless journey, including team training, pattern development, mass service migrations, architecting, and developing new solutions. Speak to a Guru today.

Sharing code efficiently across different parts of an application can be challenging with AWS Lambda, especially when using Amplify. Today, I’ll walk you through a solution to this common problem, without relying on Lambda Layers or private NPM repositories.

The Context

In my previous post about Lambda Layers [1], I delved into its limitations, especially when compared with traditional package managers like NPM. For most use cases, Lambda Layers isn’t the optimal solution for sharing code between Lambda functions.

Within a single project or service, the straightforward approach is to modularize the shared code and reference them directly.

Most popular deployment tools, including the Serverless Framework, SAM, and CDK, support this.

Unfortunately, this doesn’t work for Amplify users.

The Problem with Amplify

When using Amplify’s amplify add command to create a function, the tool manages it and only bundles contents inside the function’s src folder.

This strategy, while effective in minimizing bundle size and cold start duration, makes it hard to include local modules outside the src directory.

Consequently, sharing code across functions within the same project becomes a challenge.

The official documentation [2] suggests publishing shared code into a Lambda layer, but this requires an additional deployment cycle every time you update shared code. Plus, local mocking of functions using a layer isn’t supported by amplify mock.

An alternative could be publishing shared code as an NPM package. While this solves the mocking issue, the extra deployment cycle remains.

Another potential solution is using symlinks or installing shared modules as local packages. In a monorepo setup, this works wonders for the purpose of sharing code [3].

However, it falls short with Amplify. The bundling process in Amplify doesn’t replace symlinks with actual content, leading to runtime errors.

So, what’s the solution?

A Workaround for Amplify Users

You can harness a little-known Amplify feature [4] that allows you to execute a custom script post npm install in the function’s src directory. Using this, you can replace symlinks with their referenced content. Here’s a step-by-step guide:

1. Setting up shared modules: Consider a function named getItems and a local package called lib-utils containing shared modules.

Use an index.js as the main module for easy access:

module.exports = {
  helpers: require('./helpers'),
  utils: require('./utils')

2. Installing shared modules: In the getItems function’s src directory, run npm install ../../../lib to bring in the shared modules.

This adds a symlink called lib-utils in the node_modules folder, pointing to the lib folder where our shared modules reside.

3. Using shared modules: The shared modules can be imported in the getItems function like this:

const { utils } = require('lib-utils')

module.exports.handler = async (event) => {
  const x = utils.doSomething()

4. Custom scripting: Add a package.json at the project root and define a script, amplify:getItems, which effectively replaces the symlink with the actual content.

  "scripts": {
    "amplify:getItems": "cd amplify/backend/function/getItems/src/node_modules && cp -RL lib-utils lib-utils-temp && rm -rf lib-utils && mv lib-utils-temp lib-utils"

For every new function added, simply repeat steps 2–4.

Closing Thoughts

This is the most efficient workaround I’ve discovered. Ideally, the Amplify team will introduce a more seamless way to share code between functions. Simply replacing symlinks during bundling would be a huge step in the right direction.

If you have insights or alternative solutions for sharing code in Amplify, I’d love to hear from you. Lastly, a big shout-out to Ville Raukola for sparking this discussion during the Production-Ready Serverless workshop [5]. It’s an intensive course that can transform a novice into a serverless expert in just four weeks!


[1] Lambda layer: not a package manager, but a deployment optimization

[2] Reuse code & assets using layers

[3] AWS Lambda: how to share code between functions in a monorepo

[4] Amplify build options

[5] Production-Ready Serverless workshop