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.
Development
The development process of GCF is not as simple as I expected. After some time I managed to establish this process.
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.
Another problem was that after building code using the TypeScript compiler I still have original paths to local packages, see the line 7.
To solve this issue I decided to use the module-alias
package and custom “entry point” file to build the function code.
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)
The example repo was updated with new commands and improvements
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