Web API – Node Style

In the last four articles I’ve been doing a study of what it takes to do a node app. For those who are starting here, I had a list of requirements:

  1. I need to be able to run a small web server
  2. I need to be able to handle templated views with server-side code
  3. I need to be able to do social authentication
  4. I need to be able to use an MVC architecture
  5. I need to be able to provide a Web API
  6. I need to be able to publish a node app to Azure
  7. I need to be able to edit node applications in Visual Studio 2015

Todays article is all about the Web API. If you remember, I am developing a Dungeons and Dragons Character Sheet app as a side project. One of the things you need to do is to list and look up spells for your character. So I need a spell reference. To do this, I’m going to add two routes to my project: /api/spells to list all spells and /api/spells/number to get information on a specific spell. This is, after all, just to demonstrate what is required to do an unauthenticated Web API in Node.

I’ve got some spell data in data/spells.json – it’s just a JSON file with a bunch of fields. I could just as easily push this into a database (like SQL Server or MongoDB), but it’s small enough that I can load it at runtime. The json file itself is an array with objects – one for each spell. Each spell has an _id field that uniquely identifies the object.

Don’t like spells? Well, you can replace “spells” with products, books, companies, or whatever you desire.

To start the API, I’ve introduced an API controller in controllers/api.js:

/*eslint-env node */

"use strict";

var express = require("express"),
    passport = require("passport"),
    path = require("path"),
    config = require("../config.json"),
    spells = require("../data/spells.json");

var router = express.Router(), // eslint-disable-line new-cap
    controller = path.basename(__filename, ".js");

// Set up the Web API Here

module.exports = router;

This is mostly boilerplate code – the same code runs inside the home controller and the account controller. It uses the same routing mechanism, so I can set up my routes just the same way. In fact, the only real difference is that I need to return JSON data rather than HTML.

The /api/spells route is easy, but the /api/spells/number route requires a little explaining. Let’s get the list function out the way first:

function get_all_spells(req, res) {

router.get("/spells", get_all_spells);

There is no view in the Web API world. Instead, Express knows how to send JSON directly. In this case, I set the HTTP Response code to 200 (which means success) and then dump out the spells list that I loaded earlier.

Now, back to that special case. Here is the route:

router.get("/spells/:id", get_one_spell);

Here I’m using a standard notation for a route – whatever route segment comes after the colon will be placed into the req.params object and named the same thing. In this case, I am going to get a req.params.id as a string in my function call:

function get_one_spell(req, res) {
  // Figure out what ID we are actually looking for.  If it is non-numeric,
  // then throw a "Not Found" error back to the user.
  try {
    if (req.params && req.params.id) {
      var id = parseInt(req.params.id);
    } else {
      res.status(404).json({ error: "Spell ID must be numeric" });
  } catch (e) {
    res.status(404).json({ error: "Spell ID is always a number" });

  // Find the spell by ID - returns empty array if not found
  var spell = spells.filter(function(o) {
    return o._id === id;
  if (spell === undefined || spell.length === 0) {
    res.status(404).json({ error: "Spell ID Not Found" });
  } else {

Step 1 in this method is to convert the thing we get into a number. However, you should never trust the user. The ID may be non-numeric, missing, a space, special characters – pretty much anything. So the code needs to handle those cases. The try-catch block converts what the user gives me to an integer and returns an error to the user in all other cases.

Now that I have an integer, I can try to find the spell. I’m using Array.filter() here. If node was running ECMAScript 6 then I could use Array.find(), but Node is ECMAScript 5 right now. Array.filter() returns an empty array if it doesn’t find any matches. It could be more than one match, but that is improbable if we properly format the data.

If the returned array is undefined or the length is zero, then I tell the user that the spell is not found, otherwise return success (HTTP Response Code 200) and dump the JSON object.

Right now, this is unauthenticated, so I can just test the API using Postman (available on Chrome) or another Web REST API tool:



The Web API I’ve developed is unauthenticated right now – anyone can consume it. I probably want it authenticated. I can do this two ways – firstly, I can use the req.isAuthenticated() to adjust the output. For instance, I may want to provide everyone with a set of data, but provide a minimal set of data to everyone and extra data to authenticated users. I can do this in the get_all_spells() method like this:

function get_all_spells(req, res) {
  // If the user is not authenticated, only show spells on level
  // 1 or the cantrips.
  if (!req.isAuthenticated()) {
    var spell_list = spells.filter(function(o) {
      return (o.Level === "Cantrip" || o.Level === 1);
  } else {
    // User is authenticated - show all the spells

Simulating Authentication in Postman

One of the biggest hurdles for me was this. How do I test my authenticated API code? For that matter, what does an Unauthenticated vs. Authenticated Web API look like? To check this out, I took a look at an authentication request via node in Fiddler.

When I browse to my home page the first time, this is what I get:


The request for / results in a 302 Redirect to /home, which results in a 302 Redirect to /account/login which then returns the view and the view requires a couple of CSS pages. Clicking on Twitter gets me the following:


I’m using Auth0, so clicking on the Twitter Icon really loads my Auth0 page, which then eventually loads Twitter. Once I’ve done the twitter authentication, I’m back at the /account/external-callback URI before redirecting back to the home. Let’s take a look at that final request for the home page:


Note the connect.sid cookie – this is what authenticates the session. I need to include that in any authenticated session.

Let’s say I don’t want the use to be able to use the /api/spells/:id unless authenticated. There is no consensus on how to implement authentication in Web APIs:

  1. Return a 302 “Redirect” to the login page
  2. Return a 511 “Network Authentication Required” with a list of authentication providers
  3. Accept an Authorization header with the information and return 301 “Unauthenticated” if it isn’t provided

If a user tries to use the /api/spells/:id route unauthenticated, I’m going to return a 302 Redirect to /account/login. My code now looks like this:

function get_one_spell(req, res) {
  // If the user is not authenticated, tell them to authenticate
  if (!req.isAuthenticated()) {

  // Rest of the get_one_spell method

When I run this through the process in Postman, I can see the following:


This isn’t very friendly. When I’m using an API, I don’t want to see HTML that is meant for a human. Let’s instead look at option #2 (the 511 Network Authentication Required version):

function get_login_api() {
  var connections = config.passport.auth0.connections || [],
      r = {}, 
      buildurl = function(provider) {
        var server = config.server.uri || "http://localhost:3000";
        var url = "https://" + config.passport.auth0.domain + "/authorize" +
          "?response_type=code&scope=openid%20profile" +
          "&client_id=" + config.passport.auth0.clientid +
          "&redirect_uri=" + server + "/" + controller + "/external-callback" +
          "&connection=" + provider;
        return url;
  for (var i = 0; i < connections.length; i++) {
    r[connections[i].replace("-","_")] = buildurl(connections[i]);
  return r;

function get_one_spell(req, res) {
  // If the user is not authenticated, tell them to authenticate
  if (!req.isAuthenticated()) {
  // Rest of the get_one_spell method

The get_login_api() converts the configuration we have set up to be an object that we can serialize to JSON. When I run the request in Postman, I get the following:


I’ve obfuscated the client ID. However, you can see that this returns JSON I can work with. A list of the providers and their respective links is provided. As part of a richer login scheme – for instance, with a Single Page Application (SPA) – I can see this working.

Which one you provide is up to you. I like the 511 response code when you are using social logins with a SPA. I’ve seen the 301 Unauthenticated version implemented a few times as well. You have a lot more flexibility, of course, if your interface is only being used by your application.

As always, my code is up on my GitHub Repository.