Serverless MEAN Stack Applications with Cloud Run and MongoDB Atlas
Abirami Sukumaran, Stanimira Vlaeva8 min read • Published Jun 13, 2022 • Updated Apr 02, 2024
Rate this tutorial
As modern application developers, we’re juggling many priorities: performance, flexibility, usability, security, reliability, and maintainability. On top of that, we’re handling dependencies, configuration, and deployment of multiple components in multiple environments and sometimes multiple repositories as well. And then we have to keep things secure and simple. Ah, the nightmare!
This is the reason we love serverless computing. Serverless allows developers to focus on the thing they like to do the most—development—and leave the rest of the attributes, including infrastructure and maintenance, to the platform offerings.
In this read, we’re going to see how Cloud Run and MongoDB come together to enable a completely serverless MEAN stack application development experience. We'll learn how to build a serverless MEAN application with Cloud Run and MongoDB Atlas, the multi-cloud developer data platform by MongoDB.
All serverless platform offer exciting capabilities:
- Event-driven function (not a hard requirement though)
- No-infrastructure maintenance
- Usage-based pricing
- Auto-scaling capabilities
Cloud Run stands out of the league by enabling us to:
- Package code in multiple stateless containers that are request-aware and invoke it via HTTP requests
- Only be charged for the exact resources you use
- Support any programming language or any operating system library of your choice, or any binary
However, many serverless models overlook the fact that traditional databases are not managed. You need to manually provision infrastructure (vertical scaling) or add more servers (horizontal scaling) to scale the database. This introduces a bottleneck in your serverless architecture and can lead to performance issues.
MongoDB launched serverless instances, a new fully managed, serverless database deployment in Atlas to solve this problem. With serverless instances you never have to think about infrastructure — simply deploy your database and it will scale up and down seamlessly based on demand — requiring no hands-on management. And the best part, you will only be charged for the operations you run. To make our architecture truly serverless, we'll combine Cloud Run and MongoDB Atlas capabilities.
The MEAN stack is a technology stack for building full-stack web applications entirely with JavaScript and JSON. The MEAN stack is composed of four main components—MongoDB, Express, Angular, and Node.js.
- MongoDB is responsible for data storage.
- Express.js is a Node.js web application framework for building APIs.
- Angular is a client-side JavaScript platform.
- Node.js is a server-side JavaScript runtime environment. The server uses the MongoDB Node.js driver to connect to the database and retrieve and store data.
In the following sections, we’ll provision a new MongoDB serverless instance, connect a MEAN stack web application to it, and finally, deploy the application to Cloud Run.
Once you sign up, click the “Build a Database” button to create a new serverless instance. Select the following configuration:
Once your serverless instance is provisioned, you should see it up and running.
Click on the “Connect” button to add a connection IP address and a database user.
For this blog post, we’ll use the “Allow Access from Anywhere” setting. MongoDB Atlas comes with a set of security and access features. You can learn more about them in the security features documentation article.
Use credentials of your choice for the database username and password. Once these steps are complete, you should see the following:
Proceed by clicking on the “Choose a connection method” button and then selecting “Connect your application”.
Copy the connection string you see and replace the password with your own. We’ll use that string to connect to our database in the following sections.
Remember the Project Id for the project you created. Below is an image from https://codelabs.developers.google.com/codelabs/cloud-run-hello#1 that shows how to create a new project in Google Cloud.
- Activate Cloud Shell from the Cloud Console. Simply click Activate Cloud Shell.
- Use the below command:
gcloud services enable run.googleapis.com
We will be using Cloud Shell and Cloud Shell Editor for code references. To access Cloud Shell Editor, click Open Editor from the Cloud Shell Terminal:
Finally, we need to clone the MEAN stack project we’ll be deploying.
We’ll deploy an employee management web application. The REST API is built with Express and Node.js; the web interface, with Angular; and the data will be stored in the MongoDB Atlas instance we created earlier.
Clone the project repository by executing the following command in the Cloud Shell Terminal:
In the following sections, we will deploy a couple of services—one for the Express REST API and one for the Angular web application.
First, we’ll deploy a Cloud Run service for the Express REST API.
The most important file for our deployment is the Docker configuration file. Let’s take a look at it:
mean-stack-example/server/Dockerfile
1 FROM node:17-slim 2 3 WORKDIR /usr/app 4 COPY ./ /usr/app 5 6 # Install dependencies and build the project. 7 RUN npm install 8 RUN npm run build 9 10 # Run the web service on container startup. 11 CMD ["node", "dist/server.js"]
The configuration sets up Node.js, and copies and builds the project. When the container starts, the command “node dist/server.js” starts the service.
To start a new Cloud Run deployment, click on the Cloud Run icon on the left sidebar:
Then, click on the Deploy to Cloud Run icon:
Fill in the service configuration as follows:
- Service name: node-express-api
- Deployment platform: Cloud Run (fully managed)
- Region: Select a region close to your database region to reduce latency
- Authentication: Allow unauthenticated invocations
Under Revision Settings, click on Show Advanced Settings to expand them:
- Container port: 5200
- Environment variables. Add the following key-value pair and make sure you add the connection string for your own MongoDB Atlas deployment:
ATLAS_URI:mongodb+srv:/<username>:<password>@sandbox.pv0l7.mongodb.net/meanStackExample?retryWrites=true&w=majority
For the Build environment, select Cloud Build.
Finally, in the Build Settings section, select:
- Builder: Docker
- Docker: mean-stack-example/server/Dockerfile
Click the Deploy button and then Show Detailed Logs to follow the deployment of your first Cloud Run service!
After the build has completed, you should see the URL of the deployed service:
Open the URL and append ‘/employees’ to the end. You should see an empty array because currently, there are no documents in the database. Let’s deploy the user interface so we can add some!
Our Angular application is in the client directory. To deploy it, we’ll use the Nginx server and Docker.
Just a thought, there is also an option to use Firebase Hosting for your Angular application deployment as you can serve your content to a CDN (content delivery network) directly.
Let’s take a look at the configuration files:
mean-stack-example/client/nginx.conf
1 events{} 2 3 http { 4 5 include /etc/nginx/mime.types; 6 7 server { 8 listen 8080; 9 server_name 0.0.0.0; 10 root /usr/share/nginx/html; 11 index index.html; 12 13 location / { 14 try_files $uri $uri/ /index.html; 15 } 16 } 17 }
In the Nginx configuration, we specify the default port—8080, and the starting file—
index.html
.mean-stack-example/client/Dockerfile
1 FROM node:17-slim AS build 2 3 WORKDIR /usr/src/app 4 COPY package.json package-lock.json ./ 5 6 # Install dependencies and copy them to the container 7 RUN npm install 8 COPY . . 9 10 # Build the Angular application for production 11 RUN npm run build --prod 12 13 # Configure the nginx web server 14 FROM nginx:1.17.1-alpine 15 COPY nginx.conf /etc/nginx/nginx.conf 16 COPY --from=build /usr/src/app/dist/client /usr/share/nginx/html 17 18 # Run the web service on container startup. 19 CMD ["nginx", "-g", "daemon off;"]
In the Docker configuration, we install Node.js dependencies and build the project. Then, we copy the built files to the container, configure, and start the Nginx service.
Finally, we need to configure the URL to the REST API so that our client application can send requests to it. Since we’re only using the URL in a single file in the project, we’ll hardcode the URL. Alternatively, you can attach the environment variable to the window object and access it from there.
mean-stack-example/client/src/app/employee.service.ts
1 @Injectable({ 2 providedIn: 'root' 3 }) 4 export class EmployeeService { 5 // Replace with the URL of your REST API 6 private url = 'https://node-express-api-vsktparjta-uc.a.run.app'; 7 …
We’re ready to deploy to Cloud Run! Start a new deployment with the following configuration settings:
- Service Settings: Create a service
- Service name: angular-web-app
- Deployment platform: Cloud Run (fully managed)
- Authentication: Allow unauthenticated invocations
For the Build environment, select Cloud Build.
Finally, in the Build Settings section, select:
- Builder: Docker
- Docker: mean-stack-example/client/Dockerfile
Click that Deploy button again and watch the logs as your app is shipped to the cloud! When the deployment is complete, you should see the URL for the client app:
Open the URL, and play with your application!
The steps covered above can alternatively be implemented from Command Shell as below:
Step 1: Create the new project directory named “mean-stack-example” either from the Code Editor or Cloud Shell Command (Terminal):
mkdir mean-stack-demo
cd mean-stack-demo
Step 2: Clone project repo and make necessary changes in the configuration and variables, same as mentioned in the previous section.
Step 3: Build your container image using Cloud build by running the command in Cloud Shell:
gcloud builds submit --tag gcr.io/$GOOGLECLOUDPROJECT/mean-stack-demo
$GOOGLE_CLOUD_PROJECT is an environment variable containing your Google Cloud project ID when running in Cloud Shell.
Step 4: Test it locally by running:
docker run -d -p 8080:8080 gcr.io/$GOOGLE_CLOUD_PROJECT/mean-stack-demo
and by clicking Web Preview, Preview on port 8080.
Step 5: Run the following command to deploy your containerized app to Cloud Run:
gcloud run deploy mean-stack-demo --image
gcr.io/$GOOGLECLOUDPROJECT/mean-stack-demo --platform managed --region us-central1 --allow-unauthenticated --update-env-vars DBHOST=$DB_HOST
a. –allow-unauthenticated will let the service be reached without authentication.
b. –platform-managed means you are requesting the fully managed environment and not the Kubernetes one via Anthos.
c. –update-env-vars expects the MongoDB Connection String to be passed on to the environment variable DBHOST.
Hang on until the section on Env variable and Docker for Continuous Deployment for Secrets and Connection URI management.
d. When the deployment is done, you should see the deployed service URL in the command line.
e. When you hit the service URL, you should see your web page on the browser and the logs in the Cloud Logging Logs Explorer page.
If you’re looking to automate the process of building and deploying across multiple containers, services, or components, storing these configurations in the repo is not only cumbersome but also a security threat.
- For ease of cross-environment continuous deployment and to avoid security vulnerabilities caused by leaking credential information, we can choose to pass variables at build/deploy/up time.--update-env-vars allows you to set the environment variable to a value that is passed only at run time. In our example, the variable DBHOST is assigned the value of $DB_HOST. which is set as DB_HOST = ‘<>’.Please note that unencoded symbols in Connection URI (username, password) will result in connection issues with MongoDB. For example, if you have a $ in the password or username, replace it with %24 in the encoded Connection URI.
- Alternatively, you can also pass configuration variables as env variables at build time into docker-compose (docker-compose.yml). By passing configuration variables and credentials, we avoid credential leakage and automate deployment securely and continuously across multiple environments, users, and applications.
MongoDB Atlas with Cloud Run makes for a truly serverless MEAN stack solution, and for those looking to build an application with a serverless option to run in a stateless container, Cloud Run is your best bet.
Now that you have learnt how to deploy a simple MEAN stack application on Cloud Run and MongoDB Atlas, why don’t you take it one step further with your favorite client-server use case? Reference the below resources for more inspiration:
- Cloud Run HelloWorld: https://codelabs.developers.google.com/codelabs/cloud-run-hello#4
- MongoDB - MEAN Stack: https://mongodb.prakticum-team.ru/languages/mean-stack-tutorial
If you have any comments or questions, feel free to reach out to us online: Abirami Sukumaran and Stanimira Vlaeva.