Instantly share code, notes, and snippets.

@hone /getting_started.md Secret
Last active Dec 10, 2018

Embed
What would you like to do?
Getting Started with Single Page Apps on Heroku

Introduction

This tutorial will have you deploying a Single Page App in minutes.

Hang on for a few more minutes to learn how it all works, so you can make the most out of Heroku.

The tutorial assumes that you have:

Set up the toolbelt

In this step you will install the Heroku Toolbelt. This provides you access to the Heroku Command Line Interface (CLI), which can be used for managing and scaling your applications and add-ons. A key part of the toolbelt is the heroku local command, which can help in running your applications locally.

Download the Heroku Toolbelt

Once installed, you can use the heroku command from your command shell.

On Windows, start the Command Prompt (cmd.exe) or Powershell to access the command shell.

Log in using the email address and password you used when creating your Heroku account:

$ heroku login
Enter your Heroku credentials.
Email: me@example.com
Password:
...

Authenticating is required to allow both the heroku and git commands to operate.

Note that if you’re behind a firewall that requires use of a proxy to connect with external HTTP/HTTPS services, you can set the HTTP_PROXY or HTTPS_PROXY environment variables in your local development environment before running the heroku command.

Install the Static CLI Plugin

In order to take advantage of this beta product, we provide a set of commands for the Heroku Toolbelt in the static namespace. To get this, you'll need to install the heroku-cli-static plugin.

$ heroku plugins:install heroku-cli-static

Prepare the app

In this step, you will prepare a simple application that can be deployed.

Execute the following commands to clone the sample application:

$ git clone https://github.com/heroku/wywh.git
$ cd wywh

You now have a functioning git repository that contains a simple application.

Run the app locally

To get the app running, you first need the dependencies for your application. The way this app is setup, the following command will install all the dependencies as well as build the assets into a public/ directory. We'll be using npm which is the node package manager.

$ NODE_ENV=production npm install
> node-sass@3.4.2 install /home/hone/Projects/heroku_work/frontend/apps/wywh/node_modules/node-sass
> node scripts/install.js
...
> heroku-event-signup@1.0.0 postinstall /home/hone/Projects/heroku_work/frontend/apps/wywh
> webpack

Hash: 498f5ec33dc0481ab623
Version: webpack 1.12.13
Time: 21864ms
                                 Asset       Size  Chunks             Chunk Names
  f4769f9bdb7466be65088239c12046d1.eot    20.1 kB          [emitted]
  89889688147bd7575d6327160d64e760.svg     109 kB          [emitted]
  87faad85511847d6d753bd02ab2e607a.svg    1.69 kB          [emitted]
  6df9140f6920c8de454cee4a491a0e01.svg    4.41 kB          [emitted]
  e18bbf611f2a2e43afc071aa2f4e1512.ttf    45.4 kB          [emitted]
 fa2772327f55d8198301fdb8bcfc8158.woff    23.4 kB          [emitted]
448c34a56d699c29117adc64c43affeb.woff2      18 kB          [emitted]
          heroku-event-signup.1.0.0.js     755 kB       0  [emitted]  main
         heroku-event-signup.1.0.0.css    16.1 kB       0  [emitted]  main
                            index.html  699 bytes          [emitted]
   [0] multi main 28 bytes {0} [built]
    + 908 hidden modules
Child extract-text-webpack-plugin:   
                                     Asset     Size  Chunks             Chunk Names
      f4769f9bdb7466be65088239c12046d1.eot  20.1 kB          [emitted]
      89889688147bd7575d6327160d64e760.svg   109 kB          [emitted]
      87faad85511847d6d753bd02ab2e607a.svg  1.69 kB          [emitted]
      6df9140f6920c8de454cee4a491a0e01.svg  4.41 kB          [emitted]
      e18bbf611f2a2e43afc071aa2f4e1512.ttf  45.4 kB          [emitted]
     fa2772327f55d8198301fdb8bcfc8158.woff  23.4 kB          [emitted]
    448c34a56d699c29117adc64c43affeb.woff2    18 kB          [emitted]
        + 10 hidden modules
...

Now start your application locally using the heroku local command, which was installed as part of the Toolbelt:

$ heroku local web
forego | starting web.1 on port 5000
web.1  | Server running at: http://x220:5000

Just like Heroku, heroku local examines the Procfile to determine what to run.

Open http://localhost:5000 with your web browser. You should see your app running locally.

To stop the app from running locally, in the CLI, press Ctrl+C to exit.

Deploy the app

In this step you will deploy the app to Heroku.

Create an app on Heroku, which prepares Heroku to receive your source code.

$ heroku create
Creating sharp-rain-871... done, stack is cedar-14
http://sharp-rain-871.herokuapp.com/ | https://git.heroku.com/sharp-rain-871.git
Git remote heroku added

When you create an app, a git remote (called heroku) is also created and associated with your local git repository.

Heroku generates a random name (in this case sharp-rain-871) for your app, or you can pass a parameter to specify your own app name.

Express is used for serving assets during local development. To deploy this application on Heroku, you'll be using the static buildpack to replace the express server in production. Buildpacks handle the glue code for a specific language or use case. In this case, the static buildpack is used for serving your static assets. To set the buildpack, you need to run:

$ heroku buildpacks:set https://github.com/hone/heroku-buildpack-static

Setting the Root Directory

To start, you can use heroku static:init to bootstrap our static configuration file, static.json. The application builds its assets in the public/ directory.

$ heroku static:init
? Enter the directory of your app: (public_html) public
? Drop `.html` extensions from urls? No
? Path to custom error page from root directory: 
{
  "root": "public",
  "clean_urls": false
}

With the assets built from before by running npm install from above and the newly minted static.json, you're ready to deploy the app. The heroku static:deploy command, bundles up static.json along with the asset directory specified in the static.json.

$ heroku static:deploy
Uploading app
Creating build

-----> Fetching set buildpack https://github.com/hone/heroku-buildpack-static... done
-----> Static HTML app detected
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  838k  100  838k    0     0  9956k      0 --:--:-- --:--:-- --:--:-- 9983k
-----> Installed directory to /app/bin

-----> Discovering process types
       Procfile declares types     -> (none)
       Default types for buildpack -> web

-----> Compressing...
       Done: 1.3M
-----> Launching...
       Released v19
       https://sharp-rain-871.herokuapp.com/ deployed to Heroku

The application is now deployed. Ensure that at least one instance of the app is running:

$ heroku ps:scale web=1

Now visit the app at the URL generated by its app name. As a handy shortcut, you can open the website as follows:

$ heroku open

HTML5 Push State

Push State is used to manipulate the browser history, allowing rich JavaScript applications to ensure URLs continue to be useful. If you try to go to a page that isn't the index, but actually exists:

$ curl -I https://sharp-rain-871.herokuapp.com/conferences/
HTTP/1.1 404 Not Found
Connection: keep-alive
Server: nginx
Date: Wed, 10 Feb 2016 06:56:17 GMT
Content-Type: text/html; charset=UTF-8
Content-Length: 162
Via: 1.1 vegur

This can be fixed, but using a custom route. In your static.json, you can add a custom route that redirects all routes to the index.html.

{
  "root": "public",
  "clean_urls": false,
  "routes": {
    "/**": "index.html"
  }
}

With these changes, you can deploy them onto Heroku to have them take effect.

$ curl -I https://sharp-rain-871.herokuapp.com/conferences/
HTTP/1.1 200 OK
Connection: keep-alive
Server: nginx
Date: Wed, 10 Feb 2016 07:08:59 GMT
Content-Type: text/html; charset=UTF-8
Content-Length: 699
Last-Modified: Wed, 10 Feb 2016 06:41:29 GMT
Vary: Accept-Encoding
Etag: "56badb99-2bb"
Accept-Ranges: bytes
Via: 1.1 vegur

As can be seen, this is the exact same as the index route:

$ curl -I https://sharp-rain-871.herokuapp.com/
HTTP/1.1 200 OK
Connection: keep-alive
Server: nginx
Date: Wed, 10 Feb 2016 07:09:38 GMT
Content-Type: text/html; charset=UTF-8
Content-Length: 699
Last-Modified: Wed, 10 Feb 2016 06:41:29 GMT
Vary: Accept-Encoding
Etag: "56badb99-2bb"
Accept-Ranges: bytes
Via: 1.1 vegur

Building Assets on Heroku

Not only can you build locally, but you can also build your assets on Heroku during the deploy process. By doing this, you can advantage of some Heroku Flow features such as Review Apps or GitHub Sync.

You'll be using git to deploy this time instead of the heroku static:deploy command, which is built for local asset building. This means, you'll need to add the static.json file from above to git.

$ git add static.json
$ git commit -m "add static configuration"

You'll also need to remove the Procfile that has an entry for starting the Express server. Instead, the static buildpack will handle creating a web entry.

$ git rm Procfile
$ git commit -m "remove Procfile"

In order to build the assets on Heroku, you'll need to take advantage of multiple buildpacks. This will let you use the Node.js buildpack to build the assets on Heroku and then serve them with the static buildpack.

$ heroku buildpacks:clear
$ heroku buildpacks:add heroku/nodejs
$ heroku buildpacks:add https://github.com/hone/heroku-buildpack-static

With all that, you can now deploy:

$ git push heroku master
Counting objects: 8, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (7/7), done.
Writing objects: 100% (8/8), 773 bytes | 0 bytes/s, done.
Total 8 (delta 3), reused 0 (delta 0)
remote: Compressing source files... done.
remote: Building source:
remote:
remote: -----> Using set buildpack heroku/nodejs
remote: -----> Node.js app detected
remote:
remote: -----> Creating runtime environment
remote:
remote:        NPM_CONFIG_LOGLEVEL=error
remote:        NPM_CONFIG_PRODUCTION=true
remote:        NODE_ENV=production
remote:        NODE_MODULES_CACHE=true
remote:
remote: -----> Installing binaries
remote:        engines.node (package.json):  ^4.0.0
remote:        engines.npm (package.json):   ^3.0.0
remote:
remote:        Resolving node version ^4.0.0 via semver.io...
remote:        Downloading and installing node 4.3.0...
...
remote: -----> Fetching set buildpack https://github.com/hone/heroku-buildpack-static... done
remote: -----> Static HTML app detected
remote:   % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
remote:                                  Dload  Upload   Total   Spent    Left  Speed
remote: 100  838k  100  838k    0     0  11.1M      0 --:--:-- --:--:-- --:--:-- 11.2M
remote: -----> Installed directory to /app/bin
remote:
remote: -----> Discovering process types
remote:        Procfile declares types     -> (none)
remote:        Default types for buildpack -> web
remote:
remote: -----> Compressing...
remote:        Done: 32M
remote: -----> Launching...
remote:        Released v3
remote:        https://sharp-rain-871.herokuapp.com/ deployed to Heroku
remote:
remote: Verifying deploy.... done.
To https://git.heroku.com/sharp-rain-871.git
 * [new branch]      master -> master

You can use heroku open to view the newly deployed app.

Next steps

You now know how to deploy an app.

Here's some recommended reading.

  • Read the static buildpack Readme which lists all the features that can be used.
  • Read How Heroku Works for a technical overview of the concepts you’ll encounter while writing, configuring, deploying and running applications.
@anthonybrown

This comment has been minimized.

anthonybrown commented May 24, 2016

the link is broken
you can set the HTTP_PROXY or HTTPS_PROXY environment variable

@hone

This comment has been minimized.

Owner

hone commented Jul 29, 2016

@anthonybrown thanks, fixed.

@ericfr

This comment has been minimized.

ericfr commented Jun 19, 2017

getting this error on static:deploy:

heroku static:deploy
TypeError: Path must be a string. Received undefined
at assertPath (path.js:7:11)
at Object.resolve (path.js:1146:7)
at setopts (/Users/eric/.local/share/heroku/plugins/node_modules/glob/common.js:97:21)
at new GlobSync (/Users/eric/.local/share/heroku/plugins/node_modules/glob/sync.js:40:3)
at Function.globSync [as sync] (/Users/eric/.local/share/heroku/plugins/node_modules/glob/sync.js:26:10)
at /Users/eric/.local/share/heroku/plugins/node_modules/archiver-utils/file.js:62:17
at /Users/eric/.local/share/heroku/plugins/node_modules/archiver-utils/file.js:30:19
at Array.forEach (native)
at processPatterns (/Users/eric/.local/share/heroku/plugins/node_modules/archiver-utils/file.js:24:23)
at Object.file.expand (/Users/eric/.local/share/heroku/plugins/node_modules/archiver-utils/file.js:60:17)
at Object.file.expandMapping (/Users/eric/.local/share/heroku/plugins/node_modules/archiver-utils/file.js:94:8)
at /Users/eric/.local/share/heroku/plugins/node_modules/archiver-utils/file.js:159:19
at arrayMap (/Users/eric/.local/share/heroku/plugins/node_modules/lodash/lodash.js:660:23)
at Function.map (/Users/eric/.local/share/heroku/plugins/node_modules/lodash/lodash.js:9571:14)
at interceptor (/Users/eric/.local/share/heroku/plugins/node_modules/lodash/lodash.js:16970:35)
at thru (/Users/eric/.local/share/heroku/plugins/node_modules/lodash/lodash.js:8812:14) 'error'

@xavriley

This comment has been minimized.

xavriley commented Jun 26, 2017

@ericfr (or anyone else reading) the published https://www.npmjs.com/package/heroku-cli-static npm package needs to be rebuilt and released to work with the newer version of node released with the latest Heroku CLI.

As a workaround you can still install the plugin locally by running

git clone https://github.com/hone/heroku-cli-static.git
heroku plugins:link .

For anyone reading this in future, please check for new releases on npm before resorting to this command

@bhushangahire

This comment has been minimized.

bhushangahire commented Sep 21, 2017

How can I use ENV config variables?

@gabrielperales

This comment has been minimized.

gabrielperales commented Dec 4, 2017

I followed the tutorial but seems like it is not working now. I'm getting an Application Error, but I can't see any error in the logs.

If I run heroku static:deploy I just see this logs:

(node:21409) DeprecationWarning: Archiver.bulk() deprecated since 0.21.0
Uploading app
@lnmunhoz

This comment has been minimized.

lnmunhoz commented Dec 14, 2017

@gabrielperales Same issue here :/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment