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
:
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.