Deploying a TypeScript App from a Monorepo Using Turbo and Azure Functions

A step-by-step guide to deploying a TypeScript app from a monorepo using Turbo and Azure Functions.


Right, so you’ve got a monorepo, and you’re building a TypeScript app that needs to go live on Azure Functions (or AWS Lambda if that’s your preference). It can be a bit tricky to navigate this setup, especially if you’re using tools like Turbo and PNPM to manage everything. But don’t worry, I’ve got you covered with a simple step-by-step guide to get your app deployed.

Now, we did have a go at using PNPM’s deploy command, and while in theory, it should work, we didn’t have much luck with it. So, if you want to avoid banging your head against a wall, stick with this process, and you’ll be fine.

Why Are We Doing This?

When using npm workspaces, the node_modules can get hoisted, which means the dependencies are split across all your packages. This causes issues when deploying to Azure Functions, as they expect all dependencies to be in a flat node_modules folder.

PNPM uses symlinks to manage dependencies, but this doesn’t seem to play nicely with Azure Functions. The method we’re using here gets around this by installing the dependencies for the function app without hoisting. This way, all the dependencies are bundled into a flat node_modules folder, making it ready for deployment.

Bundling Shared Packages with Tsup

In our monorepo, we’re using tsup to bundle any shared packages from the monorepo into the Azure function app. Tsup is a great tool for bundling TypeScript projects, and it ensures that any shared packages you have in the monorepo get packaged into the function app properly. This avoids issues with missing dependencies or module resolution errors during runtime.

Step-by-Step Guide

Start off in the root of your monorepo:

1. Install Dependencies

First off, we need to install all the necessary packages. In your monorepo’s root directory, run:

pnpm i

This gets all your dependencies installed across the monorepo, and you’re ready for the next step.

2. Build the Project

Next, build your app:

pnpm run build

This compiles your TypeScript code, making it ready for deployment.

3. Clean Up Node Modules

Right, before you go any further, let’s clean up the node_modules folders. They can get pretty massive and clog everything up. Use npkill to clear them out:

npx npkill

It’s just a bit of housekeeping to keep everything tidy.

4. Prune the App with Turbo

Now it’s time to isolate the app you want to deploy. Turbo’s prune command is dead useful for this:

turbo prune [app-name]

This will strip away all the unnecessary parts of the monorepo, so you’re left with just what you need for this specific app.

5. Install Production Dependencies

Head into the out directory created by Turbo:

cd out

Now, we want to install only the production dependencies. Do this with:

pnpm install --frozen-lockfile --prod --node-linker=hoisted

This will install everything your app needs to run, without dragging in all the dev dependencies.

6. Zip Up Dependencies

Next, zip up your node_modules directory:

zip -r app.zip node_modules

This bundles up all your dependencies, ready for deployment.

7. Zip Up Your App Code

Now, move to your app directory and add the built code to the same zip file:

cd app/[app-name]
zip -r ../../app.zip dist/

You’ll also want to chuck in your configuration files like host.json and package.json:

host.json is MANDATORY for Azure Functions.
zip ../../app.zip host.json package.json

8. Deploy to Azure Functions

Finally, it’s time to deploy. If you’re using Azure Functions, run this command:

az functionapp deployment source config-zip -g [resource-group] -n [function-app-name] --src app.zip

This will upload your zipped-up app to Azure, and your function should be up and running shortly after.

Deploying to AWS Lambda

If you’re more of an AWS Lambda person, the process is pretty similar. Just use this command instead:

aws lambda update-function-code --function-name [lambda-function-name] --zip-file fileb://app.zip

Wrapping Up

And there you have it! You’ve deployed your TypeScript app from a monorepo using Turbo and PNPM. As I mentioned earlier, while PNPM’s deploy command didn’t work out for us, this method gets the job done smoothly. It should work for most monorepos too, as long as you stick to the process.

If you’re deploying to AWS Lambda instead of Azure, the steps are pretty much the same, just with a different final command.

Good luck with the deployment! Feel free to grab a brew after you’ve got it live—you’ve earned it.