DEV Community

Cover image for Google Cloud Functions in monorepo
Luke Czyszczonik
Luke Czyszczonik

Posted on

Google Cloud Functions in monorepo

Cover photo by alevision.co on Unsplash


The Google Cloud Functions (GCF) and serverless architecture have gained a lot of attention during the last few years. Serverless functions intend to be small and easy to use. With GCF, you can start from a single JavaScript file, and you do not need anything else.

I recently worked with the system that has ten separate Github repositories. All of those repositories are somehow connected and depend on one another. This situation decreased the development speed and experience, so I merged all the pieces into a monorepo. The migration went smooth.
The API services use the internal packages, so I didn’t need to publish them into private NPM anymore. This change increased development speed, experience, and confidence that services use the correct versions of packages. The whole codebase was written in TypeScript, which was an obvious benefit from monorepo because I could use features like path mapping.

Unfortunately, I ran into a few issues with GCFs and the use of local packages that stopped me from finishing the integration. I looked through a lot of Github Issues and StackOverflow threads and I saw that people had similar problems to mine. I tried a few solutions from those threads but none of them were good for my use case so I ended up with a different solution.

Let me describe some of the steps that I ran into to make the GCFs work with the monorepo.

Monorepo and Typescript

I used the Yarn Workspace to manage the monorepo dependencies. In the monorepo, every package and function uses TypeScript so path mapping was the way to go for me as well.

yarn workspace configuration
TypeScript path mapping configuration

Development

The development process of GCF is not as simple as I expected. After some time I managed to establish this process.

Function package.json example

In line 8. we have yarn dev script that is used in development. The script generates entry file (if needed, but this will be described below) and run two scripts. One of them is build process in watch mode. The second is responsible for re-run function to see the changes.

GCF do not support hot reloading by default.

Using local package

Up until now, development and deployment works smoothly and without any issues. To get this working, I modified the TypeScript configuration to include all the code related packages into the distribution directory. This step changed how the distribution directory looks and also affected the development and deployment process.

After that change, the distribution directory contains function source code and all related packages in the same shape that they are structured in the monorepo.

Distribution directory structure

Another problem was that after building code using the TypeScript compiler I still have original paths to local packages, see the line 7.

function distribution source code

To solve this issue I decided to use the module-alias package and custom “entry point” file to build the function code.

example of generated entrypoint

These solutions allow me to have a good local development experience and I also have the single entry point for my deployment.

GCF deployment dependencies

The last problem was missing that some dependencies and yarn.lock was missing during deployment. The function’s package.json file didn’t include the dependencies from used local packages.

To have the correct dependencies and their versions, I had to copy the yarn.lock file into the function directory and add all the used packages’ dependencies.

Summary

You can find all described solutions in the example repository: https://github.com/productbrew/gcf-monorepo

Top comments (2)

Collapse
 
czystyl profile image
Luke Czyszczonik

The example repo was updated with new commands and improvements

Collapse
 
luksys5 profile image
Lukas

Well done Luke!
I noticed though that you may want to update the README file as prepare-deploy command was removed
Also when deploying I needed to add these params --entry-point funnyWorld --runtime nodejs --trigger-http