Search code examples
node.jsvue.jsexpressheroku

Host Provider Heroku doesn't seem to pick up concurrently command in "npm run start"


Stack Overflow Question

Host Provider Heroku doesn't seem to pick up concurrently command in "npm run start". I can get this start command to work with "heroku local web" but get the below log errors when running the same thing on heroku host. I noticed that for some other node full stack projects, the backend is setup to run on startup. In these cases there is routing setup in express so that the request either routes to a backend route or is passed off to a static front endpape. My issues is that I don't have express to support this. I'm just running json-server on the backend (or trying) and my vue.js content on the front.

Is there a work around to get Heroku configured to start both the a front and backend server?

I can run the "npm run start" locally but not on Heroku: It seems like Heroku (at least by default) is configured to start a node app by running "npm run start". As a result, I have the following entry in my package.json:

package.json

"start": "concurrently \"export NODE_ENV=production\" \"json-server --watch db.json\" \"vue-cli-service serve --mode production\""

Heroku Logs:

2021-10-17T18:30:52.000000+00:00 app[api]: Build succeeded
2021-10-17T18:30:54.001782+00:00 heroku[web.1]: Starting process with command `npm start`
2021-10-17T18:30:55.002829+00:00 app[web.1]:
2021-10-17T18:30:55.002845+00:00 app[web.1]: > [email protected] start /app
2021-10-17T18:30:55.002845+00:00 app[web.1]: > concurrently "export NODE_ENV=production" "json-server --watch db.json" "vue-cli-service serve --mode production"
2021-10-17T18:30:55.002846+00:00 app[web.1]:
2021-10-17T18:30:55.226381+00:00 app[web.1]: [1] /bin/sh: 1: json-server: not found
2021-10-17T18:30:55.227075+00:00 app[web.1]: [2] /bin/sh: 1: vue-cli-service: not found
2021-10-17T18:30:55.228104+00:00 app[web.1]: [2] vue-cli-service serve --mode production exited with code 1272021-10-17T18:30:55.228540+00:00 app[web.1]: [1] json-server --watch db.json exited with code 127
2021-10-17T18:30:55.228781+00:00 app[web.1]: [0] export NODE_ENV=production exited with code 0
2021-10-17T18:30:55.238234+00:00 app[web.1]: npm ERR! code ELIFECYCLE
2021-10-17T18:30:55.238413+00:00 app[web.1]: npm ERR! errno 1
2021-10-17T18:30:55.242355+00:00 app[web.1]: npm ERR! [email protected] start: `concurrently "export NODE_ENV=production" "json-server --watch db.json" "vue-cli-service serve --mode production"`
2021-10-17T18:30:55.242419+00:00 app[web.1]: npm ERR! Exit status 1
2021-10-17T18:30:55.242477+00:00 app[web.1]: npm ERR!
2021-10-17T18:30:55.242529+00:00 app[web.1]: npm ERR! Failed at the [email protected] start script.
2021-10-17T18:30:55.242567+00:00 app[web.1]: npm ERR! This is probably not a problem with npm. There is likely additional logging output above.
2021-10-17T18:30:55.248113+00:00 app[web.1]:
2021-10-17T18:30:55.248206+00:00 app[web.1]: npm ERR! A complete log of this run can be found in:
2021-10-17T18:30:55.248255+00:00 app[web.1]: npm ERR!     /app/.npm/_logs/2021-10-17T18_30_55_242Z-debug.log 
2021-10-17T18:30:55.386385+00:00 heroku[web.1]: Process exited with status 1
2021-10-17T18:30:55.642820+00:00 heroku[web.1]: State changed from starting to crashed
2021-10-17T18:30:59.838564+00:00 heroku[router]: at=error code=H10 desc="App crashed" method=GET path="/" host=myvueapp1.herokuapp.com request_id=eaa81f4f-8d66-4dcc-92e6-5ac730cf9c9c fwd="73.181.221.158" dyno= connect= service= status=503 bytes= protocol=https
2021-10-17T18:31:00.272840+00:00 heroku[router]: at=error code=H10 desc="App crashed" method=GET path="/favicon.ico" host=myvueapp1.herokuapp.com request_id=a3108da6-5ba3-48cf-8bc4-4b5784a6d171 fwd="73.181.221.158" dyno= connect= service= status=503 bytes= protocol=https
2021-10-17T18:32:51.000000+00:00 app[api]: Build started by user mark.johnson.
2021-10-17T18:33:54.181436+00:00 heroku[router]: at=error code=H10 desc="App crashed" method=GET path="/" host=myvueapp1.herokuapp.com request_id=91cd8f28-f155-4b58-b4ac-bb1539e06413 fwd="73.181.221.158" dyno= connect= service= status=503 bytes= protocol=https
2021-10-17T18:33:54.362762+00:00 heroku[router]: at=error code=H10 desc="App crashed" method=GET path="/favicon.ico" host=myvueapp1.herokuapp.com request_id=8103d63b-9a7d-46f9-b2c1-5d662e77b539 fwd="73.181.221.158" dyno= connect= service= status=503 bytes= protocol=https
2021-10-17T18:34:35.091986+00:00 heroku[web.1]: State changed from crashed to starting
2021-10-17T18:34:34.685663+00:00 app[api]: Deploy 6abb4c8b by user mark.johnson.
2021-10-17T18:34:34.685663+00:00 app[api]: Release v4 created by user mark.johnson.
2021-10-17T18:34:38.788809+00:00 heroku[web.1]: Starting process with command `npm start`
2021-10-17T18:34:41.024848+00:00 app[web.1]: 
2021-10-17T18:34:41.024866+00:00 app[web.1]: > [email protected] start /app
2021-10-17T18:34:41.024866+00:00 app[web.1]: > concurrently "export NODE_ENV=production" "json-server --watch db.json" "vue-cli-service serve --mode production"
2021-10-17T18:34:41.024867+00:00 app[web.1]:
2021-10-17T18:34:41.548998+00:00 app[web.1]: [1]   Done
2021-10-17T18:34:41.555274+00:00 app[web.1]: [1]
2021-10-17T18:34:41.555534+00:00 app[web.1]: [1]   Resources
2021-10-17T18:34:41.555535+00:00 app[web.1]: [1]   http://localhost:3000/table1
2021-10-17T18:34:41.555535+00:00 app[web.1]: [1]   http://localhost:3000/table2
2021-10-17T18:34:41.555535+00:00 app[web.1]: [1]
2021-10-17T18:34:41.555536+00:00 app[web.1]: [1]   Home
2021-10-17T18:34:41.555536+00:00 app[web.1]: [1]   http://localhost:3000
2021-10-17T18:34:41.555536+00:00 app[web.1]: [1]
2021-10-17T18:34:41.555781+00:00 app[web.1]: [1]   Type s + enter at any time to create a snapshot of the database

Solution

  • As I said here and here, the common approach in an enterprise architecture is to have one app by repository and server.

    Imagine that your microservice (json) process needs to have more ram because handle heavy operations and since your web is an spa, does not need anything because it is just static files after npm run build. In this case, depending of the infrastructure, you could scale vertically/horizontally every artifact (api and web) easily. If you have several process in the same server, you would be backing up a little to the monolithic architecture. This is just one of the several advantages like:

    Api and Web

    In heroku and any platform, applications has its own port. Also you will have noticed that in your developer laptop:

    As you can see , you have two apps here, each one with its own port. In the future, each one will have its own domain : acme.com and acme-api.com

    Just for test

    The usage of json-server to publish a single file (db.json) as a microservice or rest api is just for testing or poc purposes. It would be crazy to use concurrently in a real application for real users.

    I used this on early development stage, when the api/microservice does not exist yet.

    When api is ready to use and has its own http domain (http://acme-api.com) served on its own server, I set this new url on my web.

    Two apps (api + web)

    If you don't want to complicate and be prepared for a real enterprise deployment, you should create two different app on your heroku account

    • acme-web with your vue code. This usually needs to have the npm run dev, npm run build and npm run start. Your start script should be something like this: "start": "vue-cli-service serve --mode production"
    • acme-api with your real api/microservice source code(database connection and other features). Also in early stage (api does not exist yet) you could put here your db.json and you start script should look like this: "start": "json-server --watch db.json"

    One app

    Anyway, if you want or need to have all (web+api) in one server or container, you should use another provider like aws, gcp, azure, etc. On that provider your concurrently command will work.

    On heroku, you can't run two diferent web process on the same dyno.

    Workaround

    As some framework does like MEAN, MERN & MEVN and other modern monolithics, you could use an express server to serve your api and your web.

    This app should look like this:

    var express = require('express');
    var jsonServer = require('json-server');
    
    var app = express();
    
    //publish the static result of npm run build (vue)
    //https://github.com/jrichardsz/nodejs-express-snippets/tree/master/simple-static-server
    app.use('/',
      express.static(path.join(__dirname, "site" || proces.env.SITE_FOLDER)),
    );
    
    //run json-server programmatically here
    //https://github.com/typicode/json-server/issues/253#issuecomment-205509836
    app.use('/api', jsonServer.router('db.json'));
    
    app.listen(3000);
    
    

    Variables by environment

    If you opt for and advanced distributed architecturem and yo have at least two environments: testing and production and you don't want to change the variables from time to time when you switch your application from your laptop to next environments dev > testing > production you will need to properly handle your environment variables using the heroku manager.