Google Cloud Functions in monorepo
Tags: gcf, monorepo, typescript, googlecloudfunctions
Published: 2022-02-16 13:25:02

_Cover photo by <a href="https://unsplash.com/@alevisionco?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">alevision.co</a> on <a href="https://unsplash.com/?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a>_

___

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](https://www.typescriptlang.org/docs/handbook/module-resolution.html#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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/auuxrmmwb3vobpv1dmx0.png)
![TypeScript path mapping configuration](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4moyhm3wqc4qr3lvjoos.png) 

### 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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zf7dshqi4kv8fx4r0is8.png)

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jrpo21n81t8kqs3rb2ic.png)
 
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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r7ajdw9s7v07tidspo70.png)

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/39q4yam4wzrxd3m77588.png)

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