The difference between ember serve and npm run start
Tags: javascript, node, ember, webdev
Published: 2021-06-27 14:16:42

# The difference between `ember serve` and `npm run start`

When you have built a single-page-application using [Ember CLI](https://github.com/ember-cli/ember-cli), you have two options for starting your app locally. You can either use the CLI's `ember serve` command directly or you can use the handy `npm` alias that is created for you after generating a new Ember app: `npm run start`

But what's the difference between those two?

## What `npm run start` is used for

First, `npm run start` allows you to create your own default boot up command for your application by e.g. passing additional parameters to `ember serve` and hide this modified booting instruction away in one, single, shorthand command via a definition in your `package.json`. This makes you type less and can make your life easier when developing your app locally.

If you want to learn more about how to modify the `ember serve` command, check out the official [Ember CLI Docs](https://cli.emberjs.com/release/advanced-use/cli-commands-reference/#emberserve).

In a fresh Ember app though, the functionality of both `ember serve` and `npm run start` will be almost identical. Emphasis on _almost_.

## Sometimes `ember serve` and `npm run start` don't behave the same way

Recently, while working on one of my side projects, I tried to boot up an Ember application using TailwindCSS by running the `ember serve` command. The build process was kicked off, but ultimately failed with the following error message:

```
Object.entries(...).flatMap is not a function
```

The stack trace pointed to issues with the `PostCSS` compiler I had been using to integrate TailwindCSS with my app's styles:

```
Object.entries(...).flatMap is not a function
    at PostcssCompiler.Plugin (/home/jayjayjpg/Documents/projects/my/project/node_modules/broccoli-plugin/index.js:7:31)
    at new CachingWriter (/home/jayjayjpg/Documents/projects/my/project/node_modules/broccoli-caching-writer/index.js:18:10)
    at new PostcssCompiler (/home/jayjayjpg/Documents/projects/my/project/node_modules/broccoli-postcss-single/index.js:20:5)
    at Object.keys.map (/home/jayjayjpg/Documents/projects/my/project/node_modules/ember-cli-postcss/index.js:36:12)
// ...
```

After some debugging and double-checking my setup that I have followed from Chris Masters' [working example for TailwindCSS in an Ember app](https://github.com/chrism/emberjs-tailwind-purgecss#emberjs-tailwind-10-and-purgecss-working-example) in my `ember-cli-build.js` was correct:

```
// ember-cli-build.js
'use strict';

const EmberApp = require('ember-cli/lib/broccoli/ember-app');
const isProduction = EmberApp.env() === 'production';

const purgeCSS = {
  module: require('@fullhuman/postcss-purgecss'),
  options: {
    content: [
      // add extra paths here for components/controllers which include tailwind classes
      './app/index.html',
      './app/templates/**/*.hbs',
      './app/components/**/*.hbs'
    ],
    defaultExtractor: content => content.match(/[A-Za-z0-9-_:/]+/g) || []
  }
}

module.exports = function(defaults) {
  let app = new EmberApp(defaults, {
    postcssOptions: {
      compile: {
        plugins: [
          {
            module: require('postcss-import'),
            options: {
              path: ['node_modules']
            }
          },
          require('tailwindcss')('./app/tailwind.config.js'),
          ...isProduction ? [purgeCSS] : []
        ]
      }
    }
  });
  return app.toTree();
};
```

...I started to do a quick google search, to see if any other folks using TailwindCSS ran into a similar issue.

And indeed, I found a couple of issues [here](https://github.com/tailwindlabs/tailwindcss/issues/3503) and [there](https://stackoverflow.com/questions/64907613/typeerror-object-entries-flatmap-is-not-a-function) that pointed to the fact, that the `.flatMap` method used at build time of my application was a more recent feature of Node and only available from Node v11+.

I confirmed that I was still on an older version of Node, that this would explain the lacking support of the `.flatMap` function:

```
node -v
10.16.3
```

...and was enthusiastic about quickly resolving the build problem with an upgrade of Node. Since I was using [nvm](https://github.com/nvm-sh/nvm) my upgrade turned out as follows:

```
nvm install 14
nvm use 14
rm -rf node_modules/ && npm install
```

As soon as the installation finished, I tried my luck again by running `ember serve`, and to my surprise - the build failed again with the very same `.flatMap is not a function` error!

Still in disbelief, I tried running `npm run start` instead and lo and behold - my app built successfully?

How could my application build fail when the build command was run directly, but still succeed when its alias was executed?

## How Node, NVM and Ember CLI work together

Once I checked on the versions of my Ember CLI and my Node installs, it became more clear, why `npm run start` and `ember serve` would behave differently:


```
node -v
v14.17.1

ember -v
ember-cli: 3.26.1
node: 10.16.3
os: linux x64
```

How come that Ember CLI was linked to an older version of Node different from my local Node version?

In my case, I've been using `nvm` to switch between different versions of Node on my machine.

When using [nvm](https://github.com/nvm-sh/nvm), it's important to be mindful on how this will affect the usage of globally installed packages. Nvm ensures that any global installs of binaries end up in a dedicated, versioned directory of `nvm` in the user's `HOME` directory. On Linux, you can verify this by checking the Ember CLI's binary location as follows:

```
whereis ember
ember: /home/jayjayjpg/.nvm/versions/node/v10.16.3/bin/ember 
```

A while ago I had installed Ember CLI globally via `npm install -g ember-cli` while on Node 10.16.3. This instructed `nvm` to store the binary in the related 10.16.3 Node directory and make it available via this Node version. Now whenever I would run `ember serve` on my command line, this outdated binary would be used, running on Node 10.16.3, regardless if I had instructed `nvm` to switch to Node v.14.17.1 or not:

```
# Switching to Node 10.16.3
nvm use 10

node -v
v10.16.3

ember -v
node: 10.16.3

# Switching to Node 14.17.1
nvm use 14

node -v
v14.17.1

ember -v
node: 10.16.3
``` 

Whenever I would run `npm run start` though, my project's [local Ember CLI version would be used](https://github.com/ember-learn/guides-source/issues/1133#issuecomment-539917741) leading to a successful app build.

The behavior of `ember serve` and `npm run start` doesn't have to differ this way though, as `nvm` provides you with a [command to migrate all of your already existing, global npm installations](https://github.com/nvm-sh/nvm#migrating-global-packages-while-installing) over to a newer version of Node and make them available when switching to said version:

```
nvm install 14 --reinstall-packages-from=10
```

After the migration I could now see my global Ember CLI binary associated with the newer Node version and residing in the path that I'd expect:

```
whereis ember
ember: /home/jayjayjpg/.nvm/versions/node/v14.17.1/bin/ember

ember -v
ember-cli: 3.26.1
node: 14.17.1
```

And finally, upon running `ember serve`, the app would build successfully using Node v.14.17.1 just as it would using `npm run start`!

## So what's the difference between `ember serve` and `npm run start`?

In a fresh Ember app and in nearly all cases, `npm run start` and `ember serve` function the exact same way. But if you have a different version of Ember CLI installed globally versus locally or if you're using version managers for Node, the outcome of these two commands may differ from each other.

This post has been inspired by [this great response](https://stackoverflow.com/a/63342128) on Stackoverflow to the question ["Is there a difference between `ember serve` and `npm start`?"](https://stackoverflow.com/questions/63340251/is-there-a-difference-between-ember-serve-and-npm-start)