# 2. Deploy application

Before going further with our API, let's deploy it, serverless way!

But we haven't done anything meaningful yet, shouldn't we integrate all our features before deploying?

Not at all! Deploying early and frequently is a factor of success in software development and a DevOps best practice.

# Go serverless

Why serverless? Why not deploy our app using a container or a managed service, like Azure App Service for instance? There are 2 main reasons for that:

  • You only pay for what you use, not for the resource allocation (and it's dead cheap)
  • It scales automatically without any additional setup

First, let's update our NestJS app into a serverless app so we can deploy it to Azure Functions.

What's Azure Functions?

It's a solution for running small pieces of code, or functions, in the cloud. You just write the code for the problem at hand in your preferred language, without worrying about the infrastructure or enclosing app to run it.

Open a terminal and run this command:

nest add @nestjs/azure-func-http

And... That's it. Really.

grandma looking surprised

Thanks to the included schematics, your server code is now ready to be deployed on Azure Functions. And the best part is that nothing was changed in your code, it can still run locally like previously.

If you take a closer look, here is what has been added:

  • main: a folder containing the Azure Functions trigger config and entry point
  • src/main.azure.ts: an alternative entry point for your server app that will be used only on Azure Functions (thus leaving the regular entry point intact).
  • Some config files in the project's root, we do not need to care about them for now.
  • A new start:azure NPM script in your package.json file, allowing to run your API locally but with the Azure Functions simulator this time.

To be able to use the command npm run start:azure, you must have installed the Azure Functions Core Tools with the prerequisites. It will provide the func CLI that you can use to test your functions and deploy them to Azure.

Now let's test our API running on a function:

npm run start:azure

If everything is working well, at some point you should see this log in the console:

Now listening on:
Application started. Press Ctrl+C to shut down.

Http Functions:

        main:  http://localhost:7071/api/{*segments}

That means that your API should be working on port 7071, let's test it again using curl:

curl http://localhost:7071/api/stories/random

# Create the resources

Now that your API is ready to run on Functions, let's deploy it to the real thing!

First, we have to create some Azure resources. For that we will use the Azure CLI commands:

# Login to Azure, should only be needed once
az login

# Create a new resource group
az group create --name funpets --location northeurope

# Create the storage account
# This name must be globally unique, so change it with your own
# /!\ ONLY lowercase alphanumeric characters are allowed here /!\
# (no underscore or hyphen)
az storage account create --name <your-funpets-storage> \
                          --resource-group funpets \
                          --kind StorageV2

# Create the function app
# This name must be globally unique, so change it with your own
az functionapp create --name <your-funpets-api> \
                      --resource-group funpets \
                      --consumption-plan-location northeurope \
                      --storage-account <your-funpets-storage>

The first thing we created is a resource group, a collection of Azure resources. It's typically used to group related resources for an application, by environment (production vs testing for example) or anything as needed.

Then we added a storage account that will be used to store our app data, files, and even application code.

Finally, we added a function app on which you will deploy your API.

# Deploy your app

To make deployment easier and faster for this workshop we will add a bit of specific configuration to your function app:

# Don't forget to change the name with your own
az functionapp config appsettings set --name <your-funpets-api> \
                                      --resource-group funpets \
                                      --settings "SCM_DO_BUILD_DURING_DEPLOYMENT=true"


Edit the .funcignore file and add a line with node_modules at the bottom.

The SCM_DO_BUILD_DURING_DEPLOYMENT=true setting will enable a "build" step for zip deployments, which for Node.js apps translates to npm install being called on the server directly. This way you don't have to package the required node_modules with your app, reducing upload times a lot.

Now that we have the resources created and configured on Azure, we can use the func CLI to deploy our API:

# Build your app
npm run build

# Create an archive from your local files and publish it
# Don't forget to change the name with the one you used previously
func azure functionapp publish <your-funpets-api> --nozip

After publishing, you should see in the console the URL you can use to invoke the function, like this:

Functions in funpets-api:
    main - [httpTrigger]
        Invoke url: https://<your-funpets-api>.azurewebsites.net/api/{*segments}

We can invoke our trusty curl command again to check the deployment using the previous URL:

curl https://<your-funpets-api>.azurewebsites.net/api/stories/random

Server deployment, done ✔️.

Pro tip #1

When working on a production app, you should not deploy directly from your local source code, but rather connect your source code repository to a continuous integration and deployment system (CI/CD). You can either do that using Azure DevOps or directly connect your repository for deployment using this command:

az functionapp deployment source config --name <your-funpets-api> \
                                        --resource-group funpets \
                                        --repo-url <your-git-repo-url>

Pro tip #2

For this workshop, we use the --nozip option for deployment to disable the Run-From-Package mode that runs the app directly from a read-only zip package. This is needed to run npm install remotely thanks to the SCM_DO_BUILD_DURING_DEPLOYMENT=true setting, to reduce upload times. But for production environments, you should avoid using that as your application startup time is slightly longer when --nozip option is used. Make sure the .funcignore file no longer contains the earlier added line with node_modules, else the zip deployment won't contain the node_modules folder.

Solution: see the code for step 2