iTranslated by AI

The content below is an AI-generated translation. This is an experimental feature, and may contain errors. View original article
💻

Quickly Deploying an Express (Node.js) + TypeScript Environment to Cloud Run

に公開
1

Introduction

I always forget this, so I'm writing it down for future reference.
In this article, we aim to deploy an Express (Node.js) + TypeScript project to Cloud Run.

Prerequisites

  • An environment where the gcloud command-line tool is enabled
  • Executed on zsh on Mac (It should work in WSL2 or Linux environments, but please adapt the commands accordingly if things don't go well in other environments)
  • Installation of Node.js and npm (preferably the latest LTS version if possible)

Creating the Express (Node.js) + TypeScript project

Create a project directory and initialize it with npm (you can mostly just press Enter for the input prompts).

 mkdir express-ts 
 cd express-ts 
 npm init 

Install the necessary modules via npm.

 npm install typescript @types/node ts-node-dev express @types/express npm-run-all 

Create tsconfig.json.

 npx tsc --init 

Rewrite the contents of tsconfig.json as you like.
The following is an example.

  • Place TypeScript source code under the src directory
  • Place transpiled JavaScript files under the dist directory

This is the configuration. Other TypeScript options have also been changed.

tsconfig.json
{
  "compilerOptions": {
    "allowJs": true,
    "target": "ES2020",
    "module": "commonjs",
    "sourceMap": true,
    "outDir": "./dist",
    "strict": true,
    "strictNullChecks": true,
    "alwaysStrict": true,
    "moduleResolution": "node",
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  },
  "include": ["src/**/*"]
}

Create the src directory under the express-ts directory and create the app.ts file.

src/app.ts
import express, { Application, Request, Response } from "express";

const app: Application = express();

app.use(express.json());
app.use(express.urlencoded({ extended: true }));

app.get("/", async (req: Request, res: Response) => {
  return res.status(200).send({
    message: "Hello World!",
  });
});

const port = process.env.PORT || 8000;
try {
  app.listen(port, () => {
    console.log(`Running at Port ${port}...`);
  });
} catch (e) {
  if (e instanceof Error) {
    console.error(e.message);
  }
}

Rewrite the main and scripts in package.json as follows:

package.json
  "main": "dist/app.js",
  "scripts": {
    "start": "node .",
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev": "ts-node-dev --respawn src/app.ts",
    "clean": "rimraf dist",
    "tsc": "tsc",
    "build": "npm-run-all clean tsc"
  },

Start Express with the following command.

 npm run dev

> express-ts@1.0.0 dev
> ts-node-dev --respawn src/app.ts

[INFO] 12:28:44 ts-node-dev ver. 2.0.0 (using ts-node ver. 10.9.1, typescript ver. 4.9.4)
Running at Port 8000...

Once the string "Running at Port 8000..." appears as shown above, access the following URL to confirm the server starts correctly:
http://localhost:8000/

Also, since the ts-node-dev package is used internally, it supports real-time source code updates (hot reload), which improves development efficiency. For example, if you change the string "Hello World!" and refresh your browser, the response string will change.

Cloud Run Settings

Create a new project on GCP and set the project ID using the following command. Manually replace [PROJECT_ID] with your GCP project ID and execute the command.

export PROJECT_ID=[PROJECT_ID]

To run Cloud Run, ensure that billing is enabled for the project in GCP.

 gcloud beta billing projects describe ${PROJECT_ID} | grep billingEnabled
billingEnabled: true

Switch the target project for the gcloud command to the new project.

 gcloud config set project ${PROJECT_ID}
Updated property [core/project].

Set the default region and platform for Cloud Run.

gcloud config set run/region asia-northeast1
gcloud config set run/platform managed

Placing the Dockerfile

Place a Dockerfile with the following content in the project directory.

Dockerfile
FROM node:lts-slim

WORKDIR /usr/src/app

ENV PORT 8080

COPY package*.json ./

RUN npm install --only=production

COPY . ./

RUN npm run build
CMD [ "npm", "start" ]

Deployment

The deployment method to Cloud Run is extremely simple.
It can be done with a single gcloud run deploy command.

If you are asked anything, please read the content.

  • Note: Since this is for verification, you should be able to proceed mostly with Enter and "y" if prompted.
 gcloud run deploy --source .
Service name (express-ts):
API [artifactregistry.googleapis.com] not enabled on project [1234567890]. Would you like to enable and retry (this will take a few minutes)?
(y/N)?  y

Enabling service [artifactregistry.googleapis.com] on project [1234567890]...

Deploying from source requires an Artifact Registry Docker repository to store built containers. A repository named [cloud-run-source-deploy] in
region [asia-northeast1] will be created.

Do you want to continue (Y/n)?  y

This command is equivalent to running `gcloud builds submit --tag [IMAGE] .` and `gcloud run deploy express-ts --image [IMAGE]`

API [run.googleapis.com] not enabled on project [1234567890]. Would you like to enable and retry (this will take a few minutes)? (y/N)?  y

Enabling service [run.googleapis.com] on project [1234567890]...
Allow unauthenticated invocations to [express-ts] (y/N)?  y

Building using Dockerfile and deploying container to Cloud Run service [express-ts] in project [express-ts-123456] region [asia-northeast1]
 Building and deploying new service... Uploading sources.
 Building and deploying new service... Uploading sources.
 Uploading sources...
  . Building Container...
 Building and deploying new service... Uploading sources.
 Building and deploying new service... Uploading sources.
X Building and deploying new service... Uploading sources.
API [cloudbuild.googleapis.com] not enabled on project [1234567890]. Would you like to enable and retry (this will take a few minutes)? (y/N)?
y

Enabling service [cloudbuild.googleapis.com] on project [1234567890]...

Deployment failed
ERROR: (gcloud.run.deploy) INVALID_ARGUMENT: could not resolve source: googleapi: Error 403: 1234567890@cloudbuild.gserviceaccount.com does not have storage.objects.get access to the Google Cloud Storage object. Permission 'storage.objects.get' denied on resource (or it may not exist)., forbidden

An error message will be displayed once as shown above. As indicated by Permission 'storage.objects.get' denied on resource (or it may not exist)., forbidden, this is a permissions issue.

If you selected "y" for API [cloudbuild.googleapis.com] not enabled on project [1234567890]. Would you like to enable and retry (this will take a few minutes)?, the permissions should now be enabled.

Don't panic, and run the following command again.

 gcloud run deploy --source .
Service name (express-ts):
This command is equivalent to running `gcloud builds submit --tag [IMAGE] .` and `gcloud run deploy express-ts --image [IMAGE]`

Allow unauthenticated invocations to [express-ts] (y/N)?  y

Building using Dockerfile and deploying container to Cloud Run service [express-ts] in project [express-ts-123456] region [asia-northeast1]
 Building and deploying new service... Deploying Revision. Waiting on revision express-ts-00001-pag.
 Uploading sources...
 Building and deploying new service... Done.
 Creating Revision... Revision deployment finished. Checking container health.
  . Routing traffic...
 Setting IAM Policy...
 Creating Revision... Revision deployment finished. Checking container health.
 Routing traffic...
Enabling service [run.googleapis.com] on project [1234567890]...
Done.
Service [express-ts] revision [express-ts-00001-pag] has been deployed and is serving 100 percent of traffic.
Service URL: https://express-ts-qwerty-an.a.run.app

Access the issued Service URL and confirm that the response is the same as the previous localhost result.

If you want to edit Cloud Run environment variables, memory, etc., go to the console at the URL below, select the service name (express-ts) > "Edit and Deploy New Revision" to configure the settings.

https://console.cloud.google.com/run

Great job!

Cleanup

Delete the project when it is no longer needed to avoid incurring costs.

  1. Delete the Google Cloud default project setting
    gcloud config unset project
  2. Delete the project
    gcloud projects delete ${PROJECT_ID}
  3. Delete the hands-on materials
    Delete the express-ts directory

References

gcp-getting-started-cloudrun/tutorial.md at main · google-cloud-japan/gcp-getting-started-cloudrun
Setting up an environment for Express and TypeScript

Note: The official Google Cloud Japan reference is particularly helpful as it describes the internal behavior of Cloud Run. If you want to learn more, please take a look when you have time.

Discussion

hogunhogun

감사합니다 !!!
ありがとうごじゃいます!!