Categories
Node.js Basics

Node.js Basics — App Configuration

Node.js is a popular runtime platform to create programs that run on it.

It lets us run JavaScript outside the browser.

In this article, we’ll look at how to start using Node.js to create programs.

Configuration

We can configure our Node app in various ways. We can use it to make our app more flexible.

JSON Files

One way to configure our Node.js app is to read the configuration from one or more JSON files.

For example, we can configure our HTTP server from a JSON file by writing:

config.json

{
  "host": "0.0.0.0",
  "port": 8000
}

index.js

const { port, host } = require('./config.json'),
  http = require('http');

http
  .createServer(function(request, response) {
  })
  .listen(port, host, function() {
    console.log('Listening on port', port, 'and host', host);
  });

We read the configuration from the config.json file.

It’ll automatically be parsed into an object when we call require with the path.

Then we can call createServer and listen methods with it.

We can store multiple configurations with it.

For example, we can write:

config.json

{
  "server": {
    "host": "0.0.0.0",
    "port": 8000
  },
  "database": {
    "host": "db1.example.com",
    "port": 27017
  }
}

index.js

const { server: { port, host } } = require('./config.json'),
  http = require('http');

http
  .createServer(function(request, response) {
  })
  .listen(port, host, function() {
    console.log('Listening on port', port, 'and host', host);
  });

We have a server and database property so that we can store the config for the HTTP server and the config.

Then we destructured the property we need for the HTTP server.

Environmental Variables

We can store our config as environment variables. This way, we can store the settings in our server’s OS or in a file and read them from there.

To do that, we can use the dotenv library.

We can install it by running:

npm i dotenv

Then we can use it by creating and .env file:

HOST=0.0.0.0
PORT=8000

index.js

const http = require('http');
require('dotenv').config()
const { PORT, HOST } = process.env;

http
  .createServer(function(request, response) {
  })
  .listen(PORT, HOST, function() {
    console.log('Listening on port', PORT, 'and host', HOST);
  });

We read the environment variables from the process.env property after we call:

require('dotenv').config()

We store the key-value pairs in the .env file and then read them with the code above into the process.env property.

Arguments

A Node app can also take in arguments.

The command-line arguments will be put into an array and assigned to the process.argv property.

It’ll contain the parts of the command split by spaces.

For example, if we run:

node server.js --port=8001

then process.argv is:

['node', 'server.js', '--port=8001']

We should set some default values if there’s no value set for the command-line argument.

For example, we can write:

const http = require('http');
const [,, PORT = 8080, HOST = '0.0.0.0'] = process.argv;
http
  .createServer(function(request, response) {
  })
  .listen(PORT, HOST, function() {
    console.log('Listening on port', PORT, 'and host', HOST);
  });

We assign the default values as we’re destructuring the command line arguments.

Conclusion

We can configure our Node app in various ways.

Methods include using JSON files, environment variables, and command-line arguments.

Categories
Node.js Basics

Node.js Basics — Error Handling

Node.js is a popular runtime platform to create programs that run on it.

It lets us run JavaScript outside the browser.

In this article, we’ll look at how to start using Node.js to create programs.

Error Handling

Most nontrivial programs will run into errors at some point.

Therefore, we got to handle them so that we can keep our programming running properly.

There’re a few ways to handle errors. One way is to use try-catch .

The others include catching them at the process or domain level.

To catch errors with try-catch , we just write:

function parseJSON(input) {  
  let json;  
  try {  
    json = JSON.parse(input);  
  } catch (error) {  
    return Promise.reject("Couldn't parse JSON");  
  }  
  return Promise.resolve(json);  
}

We tried to parse JSON in our function with the JSON.parse method.

When there’s an error, we return a rejected promise with Promise.reject with the reason for rejection.

Otherwise, we return a resolved promise with the parsed json object.

We can catch errors when the uncaughtException event is emitted.

For example, we can write:

process.on('uncaughtException', function(error) {  
  logger.fatal(error);  
  logger.fatal('Fatal error encountered, exiting now');  
  process.exit(1);  
});

We call process.on to watch for the uncaughtException event.

Then we call logger.fatal to log a fatal error.

And then call process.exit with code 1 to exit the program with an error code.

We should always exit the process following an uncaught error.

This is because the app is in an unknown state when an uncaught exception is raised.

A better way to catch errors is to catch them at the domain level.

To do that, we can use the domain package.

We install it by running:

npm i domain

Then we can use it by writing:

const Domain = require('domain');    
const domain = Domain.create();  
domain.on('error', function(error) {  
  console.log('Domain error', error.message);  
});  
domain.run(function() {  
  console.log(process.domain === domain);  
  throw new Error('Error happened');  
});

This prevents us from stopping the program whenever an error occurs.

We just catch errors happening in some domains instead of catching all errors.

We run any code that may raise errors in the domain.run callback so that we can catch errors.

If we have any async code, then we can run the async code in the domain.run method’s callback.

For example, we can write:

const Domain = require('domain');  
const domain = Domain.create();  
domain.on('error', function(error) {  
  console.log('Domain error', error.message);  
});  
process.nextTick(function() {  
  domain.run(function() {  
    throw new Error('Error happened');  
  });  
});

to run the code for the nextTick callback in the domain.run ‘s callback instead.

Conclusion

There’re several ways to handle errors in a Node.js app.

One way is to use try-catch to catch the error.

Another way is to catch the error by listening to the uncaughtException event.

A better way to catch errors is to catch them by the domain.

Categories
Node.js Basics

Node.js Basics — Debugging Apps

Node.js is a popular runtime platform to create programs that run on it.

It lets us run JavaScript outside the browser.

In this article, we’ll look at how to start using Node.js to create programs.

Logging with Morgan

To let us debug our Node.js apps easily, we can use the morgan package to add a logger into our app.

To install it, we run:

npm i morgan

Then we can use it by writing:

const Morgan = require('morgan'),
  Router = require('router'),
  http = require('http');
router = new Router();
router.use(Morgan('tiny'));

http.createServer(function(request, response) {
  router(request, response, function(error) {
    if (!error) {
      response.writeHead(404);
    } else {
      console.log(error.message, error.stack);
      response.writeHead(400);
    }
    response.end('n');
  });
}).listen(8000);

We add the Morgan middleware into our router by calling the router.use method with the Morgan('tiny') middleware.

Now we see logging in our app.

Now we can remove the console.log and write:

const Morgan = require('morgan'),
  Router = require('router'),
  http = require('http');
router = new Router();
router.use(Morgan('tiny'));

http.createServer(function(request, response) {
  router(request, response, function(error) {
    let info = process.versions;
    info = JSON.stringify(info);
    response.writeHead(200, {
      'Content-Type': 'application/json'
    });
    response.end(info);

  });
}).listen(8000);

and still get logging.

Alternatively, we can use the bunyan package to do the logging.

To install it, we run:

npm i bunyan

Then we can use it by writing:

const Bunyan = require('bunyan');
const logger = Bunyan.createLogger({
  name: 'example'
});
logger.info('Hello logging');

We require the bunyan package. Then we create our logger with the createLogger method.

The name is the name of the logger.

Then we use the logger.info method to log anything we want.

We can log items with other levels of logging by using various methods.

For example, we can write:

const Bunyan = require('bunyan');
const logger = Bunyan.createLogger({
  name: 'example'
});
logger.info('Hello logging');
logger.trace('Trace');
logger.debug('Debug');
logger.info('Info');
logger.warn('Warn');
logger.error('Error');
logger.fatal('Fatal');

Then we get object with various values of the level property logged to let us know the severity level of the log item.

We can also write:

const Bunyan = require('bunyan');
const logger = Bunyan.createLogger({
  name: 'example',
  level: Bunyan.TRACE

});
logger.info('Hello logging');
logger.trace('Trace');
logger.debug('Debug');
logger.info('Info');
logger.warn('Warn');
logger.error('Error');
logger.fatal('Fatal');

to set the level in when we call createLogger .

With Bunyan, we can also write the logged data to a file.

To do that, we write:

const Bunyan = require('bunyan');
const logger = Bunyan.createLogger({
  name: 'example',
  streams: [{
      level: Bunyan.INFO,
      path: './log.log'
    },
    {
      level: Bunyan.INFO,
      stream: process.stdout
    }
  ]
});
logger.info('Hello logging');
logger.trace('Trace');
logger.debug('Debug');
logger.info('Info');
logger.warn('Warn');
logger.error('Error');
logger.fatal('Fatal');

We add the streams property to add objects to specify where the log data goes.

The first entry of the streams array has the path property to specify the location to write to.

The 2nd object specifies that we write to stdout, which means we print the items on the screen.

Conclusion

We can add logging with various loggers.

We can use Morgan or Bunyan to add logging capabilities to our Node.js apps.

Categories
Node.js Basics

Node.js Basics — Add Authentication to an Express App with jsonwebtoken

Node.js is a popular runtime platform to create programs that run on it.

It lets us run JavaScript outside the browser.

In this article, we’ll look at how to start using Node.js to create programs.

Bearer Token Authentication

We can generate a token to identify authenticated users.

When they want to make requests to our server, they send in the auth token to our server to decode it.

If it’s decoded successfully, then we let the user make the request.

We can install the jsonwebtoken package by running:

npm i jsonwebtoken

Then we can use it by writing:

const express = require('express');
const bodyParser = require('body-parser');
const jwt = require('jsonwebtoken');

const users = {
  foo: {
    username: 'foo',
    password: 'bar',
    id: 1
  },
  bar: {
    username: 'bar',
    password: 'foo',
    id: 2
  }
}

const app = express();

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
  extended: true
}));

app.use(express.static('public'));
app.get('/', (req, res) => {
  res.sendFile('public/index.html');
});

app.post(
  '/login',
  function(req, res) {
    const {
      username,
      password
    } = req.body;
    const user = users[username];
    if (user === undefined) {
      return res.status(401).send('invalid user')
    }
    if (user.password !== password) {
      return res.status(401).send('invalid password')
    }

    const {
      id
    } = user;
    jwt.sign({
      id,
      username
    }, 'shhhhh', function(err, token) {
      res.send(token);
    });
  }
);

app.listen(3000, () => console.log('server started'));

In the /login route, we get the username and password properties that we make a request within the JSON request body.

Then we check the user in the users object.

If the username or password is invalid, then we send the status 401 with a message indicating the error.

Once everything is valid, then we create the token with the jwt.sign method.

The first argument is the data that will be in the token.

Then 2nd is the secret that we sign the token with.

The last argument is the callback that’s run when the token is created.

Then we send the token back to the client.

So if we make a POST request with the /login route and the following request body:

{
    "username": "foo",
    "password": "a"
}

We get 'invalid password' return with the 401 status.

If we make the same request with a valid body like:

{
    "username": "foo",
    "password": "bar"
}

Then we get a signed token like:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwidXNlcm5hbWUiOiJmb28iLCJpYXQiOjE2MDA2MjUzOTd9.05p7hoD3Qma4ShXIVWi0pKBQIv4wqSvEIvuNhaJtens

Once we decode it, we should get all the data in the object returned.

To verify the token, we can use the jwt.verify method. For example, we can write:

const express = require('express');
const bodyParser = require('body-parser');
const jwt = require('jsonwebtoken');

const users = {
  foo: {
    username: 'foo',
    password: 'bar',
    id: 1
  },
  bar: {
    username: 'bar',
    password: 'foo',
    id: 2
  }
}

const app = express();
const SECRET= 'shhhhh';

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
  extended: true
}));

app.use(express.static('public'));
app.get('/', (req, res) => {
  res.sendFile('public/index.html');
});

app.post(
  '/login',
  function(req, res) {
    const {
      username,
      password
    } = req.body;
    const user = users[username];
    if (user === undefined) {
      return res.status(401).send('invalid user')
    }
    if (user.password !== password) {
      return res.status(401).send('invalid password')
    }

const {
      id
    } = user;
    jwt.sign({
      id,
      username
    }, SECRET, function(err, token) {
      res.send(token);
    });
  }
);

app.get('/secret', (req, res, next) => {
  console.log(req.headers)
  jwt.verify(req.headers.authorization, SECRET, function(err, decoded) {
    if (err){
      return res.status(403).send('unauthorized')
    }
    req.user = decoded;
    next()
  });
}, (req, res)=>{
  res.json(req.user);
})

app.listen(3000, () => console.log('server started'));

We added the /secret route that has a middleware that checks the Authorization header for the token.

Then we pass that into the jwt.verify method to check the token.

The 2nd argument is the same secret string that we used to sign the token.

We need this to do the check properly.

Then once we decoded the string, we return 403 error if there’s an error.

Otherwise, we set req.user to the decoded token object so we can use it in our route.

Then we call next to call the route handler.

If we make a GET request to the /secret route, we should get the user data returned in the response.

Conclusion

We can use the jsonwebtoken package to issue and verify JSON web tokens for authentication.

Categories
Node.js Basics

Node.js Basics — Add 3rd Party Authentication to an Express App with Auth0

Node.js is a popular runtime platform to create programs that run on it.

It lets us run JavaScript outside the browser.

In this article, we’ll look at how to start using Node.js to create programs.

Set Up Our App in Auth0

Before we write our app, we have to set up our Node app in Auth0.

To create our app in Auth0, we log into Auth0, then we go to https://manage.auth0.com/dashboard/us/dev-v7h077zn/applications to go to the Applications page.

Then we click Create Application to create a new application.

Once we did that, we click on Regular Web App, then click Create.

Then we click on Node.js to create our app.

Create the Express App

Once we created our Express app, we can write the following to create our app:

const express = require('express');
const bodyParser = require('body-parser');
const { auth } = require('express-openid-connect');
const app = express();

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
    extended: true
}));

const config = {
    authRequired: false,
    auth0Logout: true,
    secret: 'a long, randomly-generated string stored in env',
    baseURL: 'http://localhost:3000',
    clientID: 'client id',
    issuerBaseURL: 'https://<domain>.us.auth0.com'
};

// auth router attaches /login, /logout, and /callback routes to the baseURL
app.use(auth(config));

// req.isAuthenticated is provided from the auth router
app.get('/', (req, res) => {
    res.send(req.oidc.isAuthenticated() ? 'Logged in' : 'Logged out');
});

app.listen(3000, () => console.log('server started'));

We install the express-openid-connect package to let us add authentication with Open ID.

To install it, we run:

npm i express-openid-connect

to install the package.

We call the app.use(auth(config)); to add the routes for login, logout, and the callback route that’s called after authentication succeeds.

Then we can go to the / route and see if we’re authenticated or not.

We check if we’re authenticated by calling the req.oidc.isAuthenticated() method.

The config object has the clientID, issuerBaseURL, baseURL, and the secret properties.

We can get all them all from the app’s page in our Auth0 account.

issuerBaseURL is the domain that our app is hosted on.

Now when we go to http://localhost:3000/login , we should be able to log in with a Google account.

Getting Logged in User’s Data

To get the data of the currently logged in user, we can write:

const express = require('express');
const bodyParser = require('body-parser');
const { auth, requiresAuth } = require('express-openid-connect');
const app = express();

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
    extended: true
}));

const config = {
    authRequired: false,
    auth0Logout: true,
    secret: 'a long, randomly-generated string stored in env',
    baseURL: 'http://localhost:3000',
    clientID: 'client id',
    issuerBaseURL: 'https://<domain>.us.auth0.com'
};

// auth router attaches /login, /logout, and /callback routes to the baseURL
app.use(auth(config));

// req.isAuthenticated is provided from the auth router
app.get('/', (req, res) => {
    res.send(req.oidc.isAuthenticated() ? 'Logged in' : 'Logged out');
});

app.get('/profile', requiresAuth(), (req, res) => {
    res.send(JSON.stringify(req.oidc.user));
});

app.listen(3000, () => console.log('server started'));

We add the /profile route with the middleware returned by the requiresAuth function to make it available only to authenticated users.

Then the req.oidc.user property has the user data.

Once we logged into the Google account, we should see the data from the /profile route.

Conclusion

We can add 3rd party auth into our Express app easily with Auth0.