Deploying NestJS API to Cloud Run using Cloud Build
Tags: node, nestjs, cloudrun, api
Published: 2021-08-02 04:34:23

NestJS is a NodeJS framework and deploying NodeJS API sometimes can be so challenging. Let's say for example you have VPS ready to be the place for your API to live. When you want to deploy your API to that VPS there are a lot of works to do. Starts from setting up environment for developing the API, then developing the actual API, configuring process manager like PM2, configuring web server like nginx and etc, etc. After a lot of works, finally your app is ready to serve.

Well, maybe some of you already get used to it, so it doesn't seems that complicated. But what about the beginner programmer? They definitely got intimidated by those steps to do (just like me in the past) :D. So if you feel the same like me in the past, then you in the right place.

Fortunately, at Google Cloud Next 2019, Google announced a serverless service where you can deploy your NodeJS API easily without worrying about ton of steps above. This service called Cloud Run.

Cloud Run basically is a fully-managed and highly scalable platform for deploying containerized app. Where "fully-managed" here means that Google takes care of the server for you, so you don't have to worry about managing and maintaining the server, and "highly scalable" here means that your service will be either increased or decreased dynamically based on the traffic to that service.

In this article, I will show you how to deploy your NodeJS API using NestJS to Google Cloud Run. We will use Docker for containerizing our application. So I assume that you have a bit knowledge of what Docker is or at least you have heard about it.

So, let's get started.

---

## Create NestJS API

So first of all, let's create our brand new NestJS app by simply run the command below on your Terminal or Command Prompt for Windows.

```bash
$ npm i -g @nestjs/cli
$ nest new <your-app-name>
```

After it finished, as you can see, there are a bunch of files generated automatically by NestJS. We are not going to touch any of these files. Instead, we want to test the API by simply running the command below.

```bash
$ yarn start:dev # if you choose yarn
$ npm run start:dev # if you choose npm
```

Then you can visit `[http://localhost:3000](http://localhost:3000)` on your favorite browser and your should see `Hello, world` showing up on the screen.

---

## Containerize NestJS API

As I mentioned before, Cloud Run is a service for deploying containerized app. It means that we should bundle our API into container by using Docker — it can be anything actually, but Docker is the most popular one — and then deploy that container to Cloud Run. 

So in case you don't know what the container is, basically container just bundle our API along with its dependencies and environments, so the API that runs on Cloud Run has the same dependencies and environments with the API that runs on our local machine.

Okay enough theory, let's containerize our API.

So the first thing that we have to do for containerizing our API is creating a file called `Dockerfile` in the root of our project directory. Then just copy and paste code below into `Dockerfile`.

```docker
FROM node:erbium-alpine3.14

WORKDIR /app

COPY package.json .

RUN yarn

COPY . .

RUN yarn build

EXPOSE 3000
CMD [ "yarn", "start:prod" ]
```

Let's take a look of what we just did here.

We just created a `Dockerfile` which is required by Docker to build image from an instructions that we wrote in that file.

Inside the `Dockerfile` we have a lot of stuff going on, let's cover them one by one.

1. `FROM node:erbium-alpine3.14` tells Docker that we are going to use node:erbium-alpine3.14 as our base image. So here, we don't have to install & configure NodeJS manually by ourselves.
2. `WORKDIR /app` tells Docker to create a directory called `/app` and redirect us to that directory. It basically quite similar to `mkdir /app && cd /app`.
3. `COPY package.json .` tells Docker to copy package.json file from our project on local computer to `/app` directory inside our container.
4. `RUN yarn` tells Docker to install all dependencies needed for our API.
5. `COPY . .` tells Docker to copy all files from our project on local computer to `/app` directory inside our container.
6. `RUN yarn build` tells Docker to build our API.
7. `EXPOSE 3000` tells Docker to open port 3000 for external access.
8. `CMD [ "yarn", "start:prod" ]` tells Docker to execute this command whenever we run our image.

Okay, we've created our `Dockerfile` but we still don't have image yet. Before we do that, since we are building NestJS app which is literally NodeJS, we have to ignore `node_modules` from being copied during building. Because, the size of `node_modules` is quite big and can slow down the performance of building an image.

In order to ignore some files or folders, we have to create another file called `.dockerignore` in the root of our project folder. After that, just copy and paste code below into `.dockerignore`.

```bash
node_modules/
.git/
```

Now we're ready to build our image, in order to build Docker image, we just have to run command below.

```bash
$ docker build -t <image_name:tag> . 
```

Let's cover above command one by one.

1. `docker build` tells Docker to build our image based on Dockerfile.
2. `-t <image_name:tag>` parameter used to specifies the name of the image and also tag (for versioning purpose) for our image.
3. `.` this 'dot' sign refers to current directory where the Docker will look for Dockerfile to build an image.

Now you can test your image by running `docker run` command.

```bash
$ docker run -it -p 3000:3000 <image-name:tag>
```

Then you can visit `[http://localhost:3000](http://localhost:3000)` and you should see the same result as before. But now your app runs on Docker container.

To stop the running container, just hit `Ctrl + c`.

---

## Host our code to GitHub

Before we deploy our code to Cloud Run, let's host our code first to Github so that we can clone this code to Google Cloud Shell to perform deployment. You can do it by yourself, but in case you don't know how, just copy and paste bellow commands and run on your terminal.

```bash
$ git init
$ git add .
$ git commit -m "my api project, finished"
$ git remote add origin <your-repository-url>
$ git branch -M main
$ git push origin main
```

---

## Deploy to Cloud Run

Alright, now we have all the requirements we need.

We've created our API and also containerized it with the help of Docker. Now, we're ready to deploy our API to Cloud Run.

Well, it quite simple I think cause we just have to do few steps to complete it :D

Okay let's deploy.

In order to deploy our API to Cloud Run, we'll use Google Cloud service called Cloud Build. This service will automate our deployment to Cloud Run.

First of all, create new project on GCP Console. Then copy the ID of your project.

Then to use Cloud Build, we have to create another file in our root project directory called `cloudbuild.yaml`. Then copy and paste following code to your `cloudbuild.yaml`.

```yaml
steps:
# Build the container image
- name: 'gcr.io/cloud-builders/docker'
  args: ['build', '-t', 'gcr.io/PROJECT_ID/IMAGE', '.']
# Push the container image to Container Registry
- name: 'gcr.io/cloud-builders/docker'
  args: ['push', 'gcr.io/PROJECT_ID/IMAGE']
# Deploy container image to Cloud Run
- name: 'gcr.io/google.com/cloudsdktool/cloud-sdk'
  entrypoint: gcloud
  args: ['run', 'deploy', 'SERVICE-NAME', '--image', 'gcr.io/PROJECT_ID/IMAGE', '--region', 'REGION', '--platform', 'managed', '--port', '3000']
images:
- gcr.io/PROJECT_ID/IMAGE
```

That just template, customize to fit your case. Don't forget to add `--port 3000` after `--platform managed`, since our app listen on port 3000.

The template code is available on Google Cloud Build documentation [here](https://cloud.google.com/build/docs/deploying-builds/deploy-cloud-run). Just head on to there, scroll until you find "Building and deploying a container" title and read it what the meaning of the above code.

Now push your `cloudbuild.yaml` to GitHub.

```bash
$ git add .
$ git commit -m "added cloudbuild.yaml file"
$ git push origin main
```

Back to your GCP Console, open Cloud Shell. Then make directory called whatever you want. I'll name it "projects" for now.

```bash
$ mkdir projects
$ cd projects
```

Clone your code from GitHub that we just created earlier.

```bash
$ git clone <your-repository-url>
$ cd <your-project-name>
```

Then finally run below command to deploy your API to Cloud Run.

```bash
$ gcloud builds submit
```

If you got permission "run.service.get" error during `gcloud builds submit` you can go [here](https://cloud.google.com/build/docs/deploying-builds/deploy-cloud-run#required_iam_permissions) and then enabled "Cloud Run Admin". After that, run again `gcloud builds submit`.

After it finished, go to Cloud Run dashboard, and click on the service that you just created.

Click "Permissions" tab and then click "+ Add".

For "New members" field, type `allUsers` and for "Role" field, select **Cloud Run Invoker**.

Click **Save**, then **Allow Public Access** and re-run `gcloud builds submit`.

We're done.

---

Alright, I think that's all for Deploy NestJS API to Cloud Run episode.

Well, this is my first article of my life. I know it's not perfect yet, I feel that :D but don't worry, I'll keep improving my writing skill.

Hopefully, you can get something new from this tutorial. Thanks for reading.

See you on the next article :D