Petros Kyriakoupersonal blog

I am a full stack web developer. Love tinkering all the time, especially anything javascript related.

How to allow external APIs to reach your development server

June 06, 2019

While working on Bookis, a need arose to add payments and I used stripe as my payment gateway. In the process of implementation I needed to also setup webhooks. Thats fine in production, but in a local environment I needed a way to "tunnel" the request from stripe to my local computer.

There are numerous free and paid options for this - in the end I used ngrok as I have used it before but this time I did not use the CLI part of it but I used it in a programmatic way.

First add ngrok to your dev dependencies

npm i -D ngrok

Now, lets create a file called ngrok.js and add the following

ngrok.js

const ngrok = require('ngrok')
const path = require('path')
const fs = require('fs')
const dotenvFilePath = path.resolve(__dirname, `./.env`)
const dotenv = require('dotenv')

if (fs.existsSync(dotenvFilePath)) {
  dotenv.config({
    path: dotenvFilePath,
    encoding: 'utf8',
  })
}

ngrok
  .connect({
    addr: process.env.PORT, // port or network address, defaultst to 80
    authtoken: process.env.NGROK_AUTH_TOKEN, // your authtoken from ngrok.com
    subdomain: process.env.NGROK_SUBDOMAIN, // reserved tunnel name https://alex.ngrok.io
    region: process.env.NGROK_REGION, // one of ngrok regions (us, eu, au, ap), defaults to us
  })
  .then(url => {
    console.log(`👩🏻‍💻  Webhook URL for INSERT_DESCRIPTION: ${url}/auth/google`)
    console.log(`💳  App URL to see the demo in your browser: ${url}/`)
  })
  .catch(err => {
    if (err.code === 'ECONNREFUSED') {
      console.log(`⚠️  Connection refused at ${err.address}:${err.port}`)
    } else {
      console.log(`⚠️  ${JSON.stringify(err)}`)
    }
    process.exit(1)
  })

OK, so what happens above? Basically you call ngrok.connect and you are good to go. You will get a random url which you can use to connect any APIs that need to connect with a local API like yours.

Some of the optional parameters I added is for paid use, others are for free/paid use like the region which you can specify for example 'eu' if you prefer to get a tunnel on a european server of ngrok or the addr which can be used to specify the port which your local API is running on.

Usage examples

Import in your server file

Wrap the ngrok.js in a function, export that function and include in a server file and call the function from there.

Ngrok and Nodemon

Now ideally, you would load ngrok and your server, but you want your server to be auto-reloaded on change since you are still developing right?

// package.json

...
"scripts": {
    "tunnel": "node ngrok.js",
    "dev":"nodemon server.js",
    "start": "npm-run-all --parallel tunnel dev"
}
...

Conclusion

And there you have it. You are now able to test seamlessly and webhooks from external APIs will be able to communicate with your local app.