Anda di halaman 1dari 31

MENU

Dashboard

Outside Resources

Homework

Exercises

Pre-Course

Angular

Bootstrap

CSS

Javascript

HTML

jQuery

Mongo
Node

Tools

Warmups

Career

MEAN Stack

Authentication

Intro

Class Instruction

SUBSCRIBE

MENU

Deploying your
MERN project with
Heroku
19 NOVEMBER 2015 on Deployment, Level 3, Lesson
Heroku is an excellent service that specializes in
making deployment of your web application quick
and painless. Fortunately for us, it also has a free tier
that will be perfect for our needs. There will only be a
little bit of setup needed to get your web app up and
running online. Let's get started.

Note: Heroku only allows you to deploy backend or full-stack


projects. You can't use Heroku for deploying frontend-only
projects without creating a small web server to serve it up. If you
want to deploy a frontend-only site, we recommend looking at the
insanely simple tool Surge. Check out our writeup on deploying a
frontend site with Surge

Creating your Heroku


app
1. Create a free Heroku account
Visit heroku.com and sign up for an account. Choose
"Node.js" under the "Primary development language"
section and click "Create Free Account." 

This will send you a confirmation email, so check


your email and click the link to activate your account.
This will take you to a page where you create and
confirm a password. Do so, and click "Set password
and log in"

2. Download and install Heroku


CLI
Heroku CLI is a command-line tool that makes it easy
to keep your web app updated. This is what allows
you to use git to push your code up and have your
website be updated instantly!

Visit the Heroku CLI download page and follow the


instructions to install it and get it set up. (If you have
Homebrew installed, installing the Heroku CLI with
Homebrew is easiest/quickest.) It'll ask you to log in
to the Heroku account you just created, and then
create a new git repository for your app and link that
repo up with your newly-created Heroku app

$ heroku login
Enter your Heroku credentials.
Email: adam@example.com
Password (typing will be hidden):
Logged in as adam@example.com
3. Ensure you have (or create) a
git repo for this project
For the deployment to work, you'll need to make sure
the project you're deploying is in its own git repo.
(It shouldn't be inside your V
School assignments folder. If it is, open finder and
drag it outside to be a sibling of
the assignments folder.)

Note: Here is a Sample Repo if you'd like to follow along


Check if this project has its own git repo:

$ cd path/to/your/project
$ ls -al
If the root folder of your project has a .git folder
show up after the ls -al command, that means this
project has its own git repo and you should be set to
move forward.

If it doesn't, we'll need to create one. Run git


init in the project root folder to initialize a new git
repository for your project.

4. Create a new application


Creating a Heroku application is like creating a space
online for your content to live. It helps to think of it
like a GitHub repository - it's a space online that will
hold your code, and when you make a change to your
code, you simply use git to push your new code up to
Heroku. The main difference is that while GitHub just
stores your actual code, Heroku will actually deploy
your code to a website, so it's ready for someone to
look at.

Heroku allows you to do most of the configuration for


any of your applications either 1. through the
dashboard on their website, or 2. through the Heroku
CLI you just installed. It's helpful to know that
whenever you make a change from one place, it's
changed everywhere. The CLI is just a nice way to
make these changes without having to leave the
Terminal.

We'll use the Heroku CLI to create a new app for us.
One thing that's nice about using the CLI to create the
app is that it will also set up a new git remote for you
and save you a step in the future.

Your app will need to have a unique name across all


of Heroku. Usually if you name it something
like "yourname-todos" it'll work, unless you've got a
really common name.

From your project root folder, run:

$ heroku create name-of-your-app


As mentioned above, this command creates a new
Heroku app (if you refresh your Heroku dashboard it
should show up now), as well as setting up the new git
remote for pushing your code to Heroku.
Until now, you've likely only ever referred to one git
remote before, traditionally called "origin" from our
local machine. (git push origin master sound
familiar?). If you followed the commands above,
Heroku just created a link to a remote repository and
called it heroku. When the times comes to deploy,
you'll run git push heroku master to deploy your
app!

Modify the project to


prepare it for
deployment
This guide takes a lot of excellent suggestions
from Dave Ceddia's article "Create React App with
Express in Production". As you can see from the intro
of that article, it mentions 3 possible ways (among
many others) of arranging the files in your app:
1. Keep them together – Express and React files sit
on the same machine (with all the React files
inside a specific client folder), and Express does
double duty: serving the React files and also
serving API requests. 
◦ e.g., a DigitalOcean VPS running Express on
port 80
2. Split them apart – Host the Express API on one
machine, and the React app on another. 
◦ e.g., React app served by Amazon S3, API
server running on a DigitalOcean VPS
3. Put the API behind a proxy – Express and React
app files sit on the same machine, but served by
different servers 
◦ e.g., NGINX webserver proxies API requests
to the API server, and also serves React static
files
All of these are legitimate ways to deploy your app.
We're choosing method 1 for this tutorial.

Move files around as necessary


Your project should have a structure where the root
project folder houses all your server-side related stuff
(modelsfolder, routes folder, package.json for the
server-side dependencies, .gitignore, etc.) One of
the folders inside your root project should be
called client. This client folder should be your
entire React application.

More visually, the structure should look something


like this:

|__client/ ** THIS IS EVERYTHING FROM


THE REACT SIDE **
|__ node_modules/
|__ tons of stuff...
|__ public/
|__ index.html
|__ favicon.ico
|__ etc.
|__ src/
|__ index.js
|__ main/
|__ App.js
|__ etc.
|__ models/
|__ user.js
|__ todo.js
|__ etc.
|__ node_modules/
|__ stuff...
|__ routes
|__ userRoutes.js
|__ todoRoutes.js
|__ etc.
|__ .gitignore
|__ package.json
|__ server.js
|__ etc.
Obviously if this is how you set up your project from
the beginning, you shouldn't need to rearrange
anything. If it isn't, move files around so it is.

For example, maybe you used to have a client folder


with everything React related in it and
a server folder with all your server code in it, both of
which were inside the same parent project folder. If
so, you would move all the files from
your server folder up one directory (so they were the
first files/folders inside the main project folder) and
delete your now-empty server folder. Assuming you
now had a client folder with everything related to
React in it, you would be set to go.

If you make changes to the folder structure, make


sure to run your app and test it out to see if
everything is still working.As needed, fix any broken
paths you might have created by moving files around.

Get your Express app to serve up


your React app
In the development environment, you've likely been
running your React app on port 3000 (by running npm
start or yarn start) and your server on whatever
port you chose by running node server.js.
However, we're going to set up Express to do double
duty - it will handle API calls like before, but it will
also serve up your React app when someone first
visits your main site. This just takes a few additions to
your main server.js file. Add the following:
// ... other imports
const path = require("path")

// ... other app.use middleware setups


app.use(express.static(path.join(__dirna
me, "client", "build")))

// ...
// Right before your app.listen(), add
this:
app.get("*", (req, res) => {
res.sendFile(path.join(__dirname,
"client", "build", "index.html"));
});

app.listen(...)
express.static is in charge of sending static files
requests to the client. So when the browser
requests logo.png from your site, it knows to look in
the build folder for that.

app.get("*") is a "catchall" route handler. It needs


to be near the bottom of your server file so that it will
only be enacted if the API routes above it don't handle
the request. It's in charge of sending the main
index.html file back to the client if it didn't receive a
request it recognized otherwise.

Modify any API URLs necessary


If you've been using something like "http://
localhost:8000/api/todos" as the URL for your
API calls (since create-react-app ran your app on
a development server on port 3000 but your server
was running on a different port like 8000), you can
simplify your URL by removing the whole origin
section from the beginning. So "http://localhost:
8000/api/todos" should be changed to just "/api/
todos".

Since your server will be running on the same


machine and port as your React app, the whole
beginning part will be assumed.

Add a proxy to the


client's package.json
When you make the above change, however, your
development approach of running the React app on
port 3000 and your server on port 8000 will be
broken. We can fix it very easily by adding a line to
the React app's package.json (the one in
the client folder):

"proxy": "http://localhost:8000"
So your whole package.json should look something
like this:

{
"name": "client",
"version": "0.1.0",
"private": true,
"dependencies": {
"axios": "^0.17.1",
"react": "^16.2.0",
"react-dom": "^16.2.0",
"react-scripts": "1.0.17"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --
env=jsdom",
"eject": "react-scripts eject"
},
"proxy": "http://localhost:8000"
}
Now when you make a request to "/api/todos" (or
whatever other endpoint), React will notice that the
request wouldn't work if it made it to "http://
localhost:3000/api/todos", so it'll instead
use "http://localhost:8000" as the proxy URL
and make API requests to that endpoint instead. Note
that this only happens in a development environment
from your local machine. When deployed, we'll access
both the site and the API from the same host URL.

Set up environment variables


Environment variables are things you can set in the
whole environment (your computer, the server's
computer, etc.) rather than specifically in your code.
In other words, they're variables that are accessible to
your whole machine, not just any one file.

Environment variables are useful for storing sensitive


information, since they only live on your machine
instead of having the values stored in the code. For
example, if you created a JWT secret and hard-coded
it into your JavaScript file, anyone looking at your
code on Github would know the secret and could grab
information they shouldn't be able to. Same with
database username/password combinations, etc.

In Node, environment variables can be accessed on


the process.env object. (You may have seen
something like process.env.PORT ||
8000 somewhere.). They conventionally are created
in SCREAMING_SNAKE_CASE.

With Heroku, you need to set the environment


variables within the Heroku context so it knows which
values to use when the project is running in Heroku.
You can do this two ways:

1. Set them up on the website


2. Set them up from the CLI
Setting environment variables on the website:
1. Open your app in Heroku
2. Go to "Settings"
3. Click "Reveal Config Vars"
4. Add a new variable and click "Add". Done!
Setting environment variables from the CLI:
1. In Terminal, type heroku config:set
VAR_NAME_GOES_HERE="value goes here"
2. Hit enter. Done!
Implement environment variables
in your code
Anywhere in your code you've set something sensitive
or something you need to change from one
environment to the next (such as a base URL which
changes depending on whether the code is running on
a server or locally). Some examples of things that
should be set in environment variables include:

• Port numbers
• JWT Secrets
• Database connection strings that include
username and password
• Base URLs (if you're serving your client and
server completely separately)
In fact, if you currently have your port hardcoded to a number
right now, you should go change it to an environment variable.
Say you had it originally like this:
app.listen(5000)

You should create a variable near the top of your file like this:
const port = process.env.PORT || 5000;

and then change your app.listen to use the port variable


instead. This is important for Heroku to work when you deploy.
You don't need to set this PORT variable up with Heroku yourself
- it will do it for you.
Up to now, you've probably been using "mongodb://
localhost/db-name" for your database connection
string. But when deploying to Heroku, you'll need to
use an online database service like MongoLab to store
your information, since Heroku doesn't have the
capability to house/maintain a database for you. That
means when developing locally you want to
use "mongodb://localhost/db-name", but when
it's deployed you want it to use the MongoLab
connection string instead.

We'll address the MongoDB/MongoLab situation


next, but for now, go through your code and add an
environment variable anywhere it makes sense. For
this class, if your app has authentication with JWTs,
you'll want to change your secretto be something
like:

const secret = process.env.SECRET ||


"some secret passphrase here for local
development"
This says to set a local variable called secret, whose
value will either be whatever the environment variable
for SECRETis set to, or (if that's undefined), a regular
string for local development. Make sure not to use
the same secret phrase in both places!

You'll also want the dotenv package. Make sure to


require it in your server.js and use it.

npm install --save dotenv


require("dotenv").config()

Set up MongoLab
Heroku has an easy-to-use addon you can include in
your project very easily that spins up a database, gives
you a user and password, and creates an environment
variable all in one command for you. While you could
create your own account and set up your own
database, we'll just use this addon to do everything for
us.

First, although it is completely free, Heroku requires


you enter payment information in your account in case
you scale your app up and use anything other than the
sandbox free version of MongoLab.

Head to heroku.com and open your Account Settings


>> Billing and add a credit/debit card. Again, it won't
charge you unless you manually go out of your way
to scale up your app beyond the free tier.

Once that's completed, from your project root, type:


$ heroku addons:create mongolab -a
<name-of-heroku-app>

You should get a message like the one above that it


created the database for you, and it gives you the
name of the environment variable. Copy that name
(MONGODB_URI in the image above) and use that in
your code where you connect to the database:

mongoose.connect(process.env.MONGODB_URI
|| "mongodb://localhost/todos")
// Or wherever you specify your database
string
Again, this means it will try to find that environment
variable and use it (in the Heroku deployment) or if it
can't (locally it's undefined) it'll use the local
connection string.

Add a .gitignore to your main


project
create-react-app automatically created
a .gitignore file for us and added a bunch of useful
things to it. However, we want to make sure all our
server stuff isn't cluttered with things too.

In your main project root, create a file


called .gitignore and add a few things we want to
make sure don't end up in our repository (assuming
you haven't already done this):

.DS_Store
node_modules/
.env
*.orig
.idea/
.vscode/
Most of those probably won't be necessary for you,
but it doesn't hurt to add them now anyway.

Teach Heroku to run a build and


run your app after deploying
You might have noticed in the React
app's .gitignore file that we're ignoring
the build folder. That's the folder that compiles
everything together into one place, static assets, all
JavaScript, etc. It gets created by running npm run
build. However, we don't want to commit
the build folder, which is why it's in
our .gitignore by default.

Fortunately, Heroku will look in package.json for a


script to run after we finish uploading the code there.
In your server's package.json, add the
following scripts section:

"scripts": {
"heroku-postbuild": "cd client &&
npm install --only=dev && npm install &&
npm run build"
}
Another thing we want Heroku to be able to do is run
our server file with node once it's built. Heroku will
look for a Heroku-specific file called a Procfile to
tell it how to do that. If no Procfile is found, it will
just run npm start. So we need to add
the start script to our scripts as well. The whole
scripts section should now look like this:
"scripts": {
"start": "node server.js",
"heroku-postbuild": "cd client &&
npm install --only=dev && npm install &&
npm run build"
}
One last thing that sometimes will cause issues has to
do with your version of node.js. We'll also teach
Heroku which version of node to use for the project.

In terminal, type node -v. It should spit out the


version of Node.js you're using.

If that version were, for example, 8.9.4, you'd add


the following to your server's package.json file:

"engines": {
"node": “8.9.4”
}
After all this, your server's package.json should
look something like this:
{
"name": "mern-to-heroku",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"dependencies": {
"body-parser": "^1.18.2",
"express": "^4.16.2",
"mongoose": "^4.13.6",
"morgan": "^1.9.0"
},
"scripts": {
"start": "node server.js",
"heroku-postbuild": "cd client
&& npm install --only=dev && npm install
&& npm run build"
},
"engines": {
"node": "8.9.2"
}
}
You should now be all set up to deploy your site
through Heroku!

Deploy!
Once you've got everything above completed, your
deployment and updating of your site should go
through seamlessly. You'll use git to create a new
commit and push to the heroku remote:

$ git status
$ git add -A
$ git commit -m "Add a commit message
here. Probably 'initial commit' would be
fine for the first time"
$ git push heroku master
Pushing to Heroku takes a little longer than pushing
code to GitHub, so give it a few extra seconds. If
everything went correctly, it should show a success
message and tell you where your app is deployed,
which is at https://<your-app-
name>.herokuapp.com/
Updating your
Heroku app
Any time you make changes to your application, all
you have to do to update the live site is run your
usual git add -A and git commit -m "message",
but instead of pushing to your GitHub account, you'll
just run git push heroku masterinstead. This will
push your new code to your running Heroku instance
and relaunch the site with your changes.

Custom Domains
If you have purchased a domain name for your site
then here is how to make your custom domain
accessible via Heroku!
Export/Import
Database
Most projects will be fine without this step, but if you
really want all your data that you have while you've
been developing, you can export your local database,
and import it to MongoLab.

(1. know your database name.

There are two ways to find this.

Get into the Mongo shell by typing mongo into


terminal 
type show dbs into the shell. Find yours.

It's also in the line in your server file in your project:

mongoDB://localhost/<db name>
(2. Export

mongodump -d <db name> -o db-backup 


cd into db-backup

(3. Import

Go to heroku.

, settings, reveal config vars, one for mongodB_uri

Import database line in terminal

In her 
overview 
mlab mongoDV (installed addon) 
will open stuff mLab 
When you added the addon, it did everything for you.
(uname and pword)

Open into your account


far right, tools

copy the import line.

mongodb://:@

You will need to click edit

V School Web Development © 2018


Proudly published with Ghost

Anda mungkin juga menyukai