domfarolino / angular2-login-seed

(deprecated) Seed app w/ Angular2, Node, Express, and OAuth login
https://domfarolino.com/angular2-login-seed
MIT License
181 stars 77 forks source link

Heroku and EB deep links not routing #16

Closed adamhpan closed 7 years ago

adamhpan commented 7 years ago

I can't get the deep links to work when I'm hosting the application on Heroku/EB. I've tried to run an nginx server to route everything to the index.html in ./dist but the deep links are still not found.

Can you run through how you hosted the project on Heroku?

domfarolino commented 7 years ago

There are technically two applications in this repository. One is for the backend which serves to static content or HTML, and the Angular portion of the app is only on the front-end. So I deployed the backend to Heroku (https://angular2-login-seed.herokuapp.com/api/....), and the front-end app is just in the gh-pages branch of this repository. When you visit https://domfarolino.com/angular2-login-seed you're hitting the static front-end angular app, and whenever the angular app needs some data to display on the page it hits the API deployed on Heroku. This make sense?

The front and back end are completely divorced, so the deep linking/routing is all done with the Angular app in the front-end.

domfarolino commented 7 years ago

It is possible to deploy them all with one app, but it is messier. You'd need the backend application to serve the same HTML file for any route that does not start with /api/ so that the Angular router could actually handle each route itself (and not let the backend serve some nonesense), and furthermore you'd need to be serving the index from the dist/ folder that contains the compiled version of the angular app.

When creating a robust single page app in something like Angular it makes sense to let Angular (with all its features and glory etc) handle just about everything except for dynamic data collection, which is what the backend does in this application. So when you deploy to Heroku you should only be deploying the back-end app, now you can serve the front-end from anywhere and just make sure the Angular app hits the correct back-end.

Let me know if you need any more info/help

adamhpan commented 7 years ago

Totally, I get that we have the Angular app and api app.

I'm having trouble with the Angular app right now. I can't seem to use any of the deep links when I only host it. The app runs fine when it first get's hit but if I refresh, the page breaks.

domfarolino commented 7 years ago

Hmm, could you give me the URL you're hosting the Angular app at?

adamhpan commented 7 years ago

Here we are https://ng-login-seed.herokuapp.com/

Here are the reproduction steps

  1. pull the repo
  2. Change the following in package.json
    • Move angular-cli to dependencies so they're installed by heroku
      • Add the following pre and post install scripts as well as the http server start "scripts": { "start": "http-server ./dist", "preinstall": "npm install -g http-server", "postinstall": "ng build --prod" },
  3. Push to heroku for deployment
  4. Navigate to the heroku page
  5. Refresh

Expected - the login screen should display correctly Actual - there's a white screen

I haven't enabled cors so none of the api calls will work.

domfarolino commented 7 years ago

Thanks for the information. http-server is a simple server in that it will serve you whatever files you're trying to get (denoted by URL). If it is anything like python -m SimpleHTTPServer it probably doesn't do much more than this (path aliasing and other things with logic). It is a dumb server, in that if a file exists it will give it to you. So if you visit /, the server will automatically look for a /index.html which exists since Angular built it. However this kind of server typically does not support deep-linking. Think about it, on the front end we've changed the URL from / => /login at some point, but we've never told the server about this. When the URL changed, Angular knew what to do and it changed the front-end content accordingly. The server however, only remembers our first request that it was able to handle successfully - /.

Fast forward to us refreshing the page after the URL changed on the front-end. Now we're actually saying, "hey server - serve us up the content at /login for us" since our URL has changed to /login. The server, http-server in this case will look for a /login folder in the ./dist folder in your case instead of serving up index.html. Since it can't find it, it will fail (different servers will fail differently). So the key to supporting deep-linking with a server is to serve the same static HTML index.html no matter what the route we request is. Why is this? Well because we've already seen that Angular knows how to deal with those links when they are given, we just need to make the server more like a sieve, so that it will always let Angular respond.

Regarding http-server, I've never used it so I don't know if there's a way to say "hey server, on request of /, /login, register, /heroes, ... serve the same index.html page no matter what". This is something you'll need to find out. With express/Node.js it is very very simple to do this.

Does this make sense why the deep-linking is failing? It is nothing to do with the Angular app, it is instead a fundamental issue that very watered-down basic http servers have when trying to serve single-page applications with routers built in. The idea is typically we want to serve the same static content no matter what request is made, and sometimes baking those rules into the server is simple.

domfarolino commented 7 years ago

For example when I try to retrieve /login when running the http-server locally in the ./dist folder, I get the following error:

[Mon Nov 21 2016 17:02:24 GMT-0500 (EST)] "GET /login" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.100 Safari/537.36" [Mon Nov 21 2016 17:02:24 GMT-0500 (EST)] "GET /login" Error (404): "Not found"

Because we've got no rule telling the server to route everything to index.html. Regarding your original issue with the nginx server that should've routed everything to index.html I'm not sure why that did not work. I would assume the server was not set up to route everything to index.html as you may have thought but I could be wrong

adamhpan commented 7 years ago

I'll try the nginx routing again.

Thanks for the help!