Cover image for The Ultimate `npm run dev`

The Ultimate `npm run dev`

lukeocodes profile image Luke Oliff ・2 min read

GitHub logo lukeocodes / express-nodemon-ngrok-starter

Express Nodemon and Ngrok - The Ultimate `npm run dev`

The Ultimate npm run dev

This is a starter app for Express.js.

The src/devApp.js runs your src/app.js using Nodemon as a module, starting Ngrok when the app is run, gracefully stopping Ngrok when the app is closed.

How Can I Use This?

src/app.js is just a standard hello-world/app.js from the Express.js site. You probably just need to start as you normally would, editing src/app.js as you build your app.

Clone this repository.

git clone https://github.com/lukeocodes/express-nodemon-ngrok-starter.git

Install the dependencies.

npm install

Production Mode

Nodemon, Ngrok and Dotenv are all devDependencies and only required inside src/devApp.js, so in production mode none of these libraries which are great for development are installed or required by the app.

Start the app without these modules.

npm start

Development Mode

Start development mode with the Nodemon, Ngrok, and Dotenv modules.

npm run dev

What Else?

You could use Livereload to…


Start with the Express Hello World, by running these commands from an empty directory. First, initialise NPM.

npm init -y

Now, install Express.

npm install express

Add this code to an app.js file.

// app.js
const express = require('express')
const app = express()
const port = process.env.PORT || 3000

app.get('/', (req, res) => res.send('Hello World!'))

app.listen(port, () => console.log(`Example app listening at http://localhost:${port}`))

Start it and check it out at localhost:3000.

node app.js

Or, if you like, add a script to your package.json.

    "scripts": {
+     "start": "node app.js"

To run it like so:

npm start

And, we're done! (Just kidding).


Now, you want to launch Ngrok so you can share your Hello World with THE world.

npm install ngrok -g
ngrok http 3000

Start it and check it out at https://your-ngrok-hash.ngrok.io.

Awesome! (this isn't it, bare with me until the end).


Now, if you've ever worked on a React.js or Vue.js project and gone back to a normal Node.js project without Webpack, you've probably wanted to be able to reboot your app automatically whenever you make changes.

For which, you've probably stumbled on Nodemon for that. Install it as a devDependency.

npm install nodemon --save-dev

A quick update to your package.json file and you can run your app with Nodemon.

    "scripts": {
+     "dev": "nodemon app.js",
      "start": "node app.js"

Now, you can run it with Nodemon like this:

npm run dev


But, what if you can pop all these into your app itself? Yeh, you read that correctly! :exploding_head_emoji:

Firstly, create a devApp.js file.

Inside that file, add this example code from Nodemon, to run it as a module.

// devApp.js
const nodemon = require('nodemon')

  script: 'app.js',
  ext: 'js'

nodemon.on('start', async () => {
  console.log('app.js just started')
}).on('quit', async () => {
  console.log('killing app.js')

Nextly, install Ngrok as a devDependency.

npm install ngrok --save-dev

Now, modify the devApp.js file and add this code.

  // devApp.js
  const nodemon = require('nodemon')
+ const ngrok = require('ngrok')
+ const port = process.env.PORT || 3000

    script: 'app.js',
    ext: 'js'

+ let url = null

  nodemon.on('start', async () => {
-   console.log('app.js just started')
+   if (!url) {
+     url = await ngrok.connect({ port: port })
+     console.log(`Server now available at ${url}`)
+   }
  }).on('quit', async () => {
-   console.log('killing app.js')
+   await ngrok.kill()

Lastly, change your package.json up a little bit!

    "scripts": {
-     "dev": "nodemon app.js",
+     "dev": "node devApp.js",
      "start": "node app.js"

Now, when you start your server, your local URL and public URL are now output in the console and when you update your code, the node app is refreshed (but you keep your ngrok URL!)

Local URL and Remote URL output when the app starts and nodemon refreshes code automatically when it is changed

Posted on by:

lukeocodes profile

Luke Oliff


60% water. Language champion. JAMStack's biggest fan.


markdown guide

I would add livereload to all that :)


Ohhh I was building an API at the time so I hadn't considered this. Great idea!


I personally use concurrently, but all are great. Either npm-run-all or concurrently run their processes side-by-side but AFAIK decoupled.

I wanted this to gracefully terminate the Ngrok session when closing node. In concurrently you can use --kill-others-on-fail but that doesn't feel very graceful :)