This guide will walk you through the process of deploying an Express.js application in the cloud. You will use Render, a Platform-as-a-Service cloud, to run your application; and MongoDB Atlas, a database cloud, to host your database.
When working as a team, only one member of the team needs to follow this guide.
- Requirements
- Name your project
- Create an Express.js application
- Connect your application to a database
- Deploy the application to Render
- Create a MongoDB cluster on MongoDB Atlas
- Provide your database URL to your Render application
- Node.js 12+
- Git
- A GitHub account
- A Render account
- A MongoDB Atlas account
"There are only two hard things in Computer Science: cache invalidation and naming things."
-- Phil Karlton
Choose a good name for your project. You will have to use it to name several things: your Express.js application, the Render application, the MongoDB cluster and database, etc.
Install the custome Express.js generator if you haven't already:
$> npm install -g yo
$> npm install -g generator-express-api-es
Generate a new Express application:
$> yo express-api-es
Make sure it works by following the instructions in your terminal.
Check that you can access the express application at http://localhost:3000.
Once you're sure it works, you can stop it with Ctrl-C
.
To deploy code on Render, you will need to use Git. Initialize a Git repository in the application's directory:
$> git init
Add a .gitignore
file to ignore the node_modules
directory (dependencies
will be automatically installed by Render when you push):
$> echo node_modules > .gitignore
The contents of your .gitignore
file should look like this:
node_modules
Commit all the app's files:
$> git add --all
$> git commit -m "Initial commit"
Register a GitHub account if you haven't already.
Create a private repository on GitHub, then add it as a remote on your machine and push your new application to it:
$> git remote add origin git@github.com:your-github-username/your-repo-name.git
$> git push -u origin master
If you have team members, you may add them in the repository's settings on GitHub so that they also have push access:
Add Mongoose to your application:
$> npm install mongoose
Then add the following code to the app.js
file:
import mongoose from 'mongoose';
mongoose.connect(process.env.DATABASE_URL || 'mongodb://localhost/your-app-name');
Note the code
process.env.DATABASE_URL || 'mongodb://localhost/your-app-name'
which will either take the value of the$DATABASE_URL
environment variable, or default tomongodb://localhost/your-app-name
if the environment variable is not available. See https://nodejs.org/api/process.html#process_process_env.
Stage all changes (including the changes made to package.json
and
package-lock.json
as a result of the npm install mongoose
command). Then
commit and deploy this change:
$> git add .
$> git commit -m "Connect to a MongoDB database with Mongoose"
$> git push origin master
Register a Render account if you haven't already. If you register through GitHub, you will not have to link the two accounts together later.
Go to your dashboard and create a new Web Service:
Connect your GitHub repository to Render by selecting the one the contains your app from the list.
Name the application, choose the region and enter the commands used to build and start your app. The branch name should automatically be set to "main" or "master", depending on how your repository is setup.
Select the free plan and finish the creation process.
Once you submit the form, Render will automatically try to deploy your app. You will be able to see live logs. Pretty cool, but be aware that deploys on the free plan can take a little while. Be patient.
The deployment process should eventually succeed. But... Oh no there seems to be a some weird error in the logs! Think about it for a second. What could've gone wrong?
2020-09-13T09:46:13.394704+00:00 app[web.1]: (node:23) UnhandledPromiseRejectionWarning: MongoNetworkError: failed to connect to server [localhost:27017] on first connect [Error: connect ECONNREFUSED 127.0.0.1:27017
2020-09-13T09:46:13.394707+00:00 app[web.1]: at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1141:16) {
2020-09-13T09:46:13.394708+00:00 app[web.1]: name: 'MongoNetworkError'
2020-09-13T09:46:13.394709+00:00 app[web.1]: }]
2020-09-13T09:46:13.394710+00:00 app[web.1]: at Pool.<anonymous> (/app/node_modules/mongodb/lib/core/topologies/server.js:438:11)
2020-09-13T09:46:13.394711+00:00 app[web.1]: at Pool.emit (events.js:315:20)
2020-09-13T09:46:13.394712+00:00 app[web.1]: at /app/node_modules/mongodb/lib/core/connection/pool.js:562:14
2020-09-13T09:46:13.394712+00:00 app[web.1]: at /app/node_modules/mongodb/lib/core/connection/pool.js:995:11
2020-09-13T09:46:13.394712+00:00 app[web.1]: at /app/node_modules/mongodb/lib/core/connection/connect.js:32:7
2020-09-13T09:46:13.394713+00:00 app[web.1]: at callback (/app/node_modules/mongodb/lib/core/connection/connect.js:280:5)
2020-09-13T09:46:13.394713+00:00 app[web.1]: at Socket.<anonymous> (/app/node_modules/mongodb/lib/core/connection/connect.js:310:7)
2020-09-13T09:46:13.394714+00:00 app[web.1]: at Object.onceWrapper (events.js:422:26)
2020-09-13T09:46:13.394714+00:00 app[web.1]: at Socket.emit (events.js:315:20)
2020-09-13T09:46:13.394715+00:00 app[web.1]: at emitErrorNT (internal/streams/destroy.js:92:8)
2020-09-13T09:46:13.394716+00:00 app[web.1]: at emitErrorAndCloseNT (internal/streams/destroy.js:60:3)
2020-09-13T09:46:13.394716+00:00 app[web.1]: at processTicksAndRejections (internal/process/task_queues.js:84:21)
Remember this piece of code?
mongoose.connect(process.env.DATABASE_URL || 'mongodb://localhost/your-app-name');
At this point, our app is looking for a DATABASE_URL
variable environment. Unfortunately we have not configured it yet and are therefore trying to connect to our local Mongo instance which is obviously inaccessible from remotely.
We must therefore setup a database elsewhere and provide its URL to Render. Let's start by setting up a MongoDB Atlas cluster.
Register a free MongoDB Atlas account for a cloud deployment:
Choose the free shared cluster plan:
Create a cluster if one has not already been created for you:
Configure your cluster. The provider and region are unimportant as long as you choose one that is free, but you should at least change the default name:
You must configure network access to your cluster to allow connections from the outside world:
For the purposes of this guide, you can allow access from anywhere, which should
set the access list entry to 0.0.0.0/0
(i.e. any source IP address is allowed
to access the cluster):
In a real production environment, you should whitelist the exact IP addresses of your servers so that only they can connect to your cluster, for improved security.
You must then create a database user to connect with:
Set the credentials for the new database user:
To obtain the connection URL for your cluster, go to Clusters and click on your cluster's Connect button:
You want to connect an application:
And you are using a Node.js driver. You should copy the provided connection URL:
Note that the connection URL is in the format
mongodb+srv://admin:<password>@your-cluster-name.abcd.mongodb.net/<dbname>?retryWrites=true&w=majority
.
There are two placeholders in this URL, <password>
and <dbname>
, which you
should replace:
<password>
is the password of the database user you just created.<dbname>
is the name of a MongoDB database to connect to. You should name it after your project. The exact name is unimportant, since MongoDB will automatically create the database the first time you connect to it.
If you have the
mongo
executable available in your command line, you can connect to your new MongoDB cluster from your machine with the command:mongosh "mongodb+srv://admin:<password>@your-cluster-name.abcd.mongodb.net/<dbname>?retryWrites=true&w=majority"
(Use the full path to
mongo.exe
on Windows instead ofmongo
.)
Now that you have a connection URL for a MongoDB database, you should give it to your Render application.
This is trivially done by adding DATABASE_URL
to your application's
environment variables in the Environment section:
These changes will only be taken into account the next time we deploy. New deploys will automatically happen when you push commits on your main branch to GitHub.
$> git add .
$> git commit -m "<exciting changes>"
$> git push origin master
Once your deploy is live, you should be able to test your API at the URL generated by Render. It should look something like: https://app_name-4vxg.onrender.com
🎉