Build a Simple REST API with Node and OAuth 2.0

Build a Simple REST API with Node and OAuth 2.0

In this article, you'll learn how to create a REST API using Node.js, and secure it with OAuth 2.0 to prevent unwarranted requests. REST APIs are all over the web, but without the proper tools require a ton of boilerplate code.

In this article, you'll learn how to create a REST API using Node.js, and secure it with OAuth 2.0 to prevent unwarranted requests. REST APIs are all over the web, but without the proper tools require a ton of boilerplate code.

JavaScript is used everywhere on the web nearly every web page will include at least some JavaScript, and even if it doesn’t, your browser probably has some sort of extension that injects bits of JavaScript code on to the page anyway. It’s hard to avoid in 2018.

JavaScript can also be used outside the context of a browser, for anything from hosting a web server to controlling an RC car or running a full-fledged operating system. Sometimes you want a couple of servers to talk to each other, whether on a local network or over the internet.

I’ll show you how to use a couple of amazing tools that make it all a breeze, including Okta to implement the Client Credentials Flow, which securely connects two machines together without the context of a user.

Build Your Node Server

Setting up a web server in Node is quite simple using the Express JavaScript library. Make a new folder that will contain your server.

$ mkdir rest-api


Node uses a package.json to manage dependencies and define your project. To create one, use npm init, which will ask you some questions to help you initialize the project. For now, you can use standard JS to enforce a coding standard, and use that as the tests.

$ cd rest-api

$ npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.

See `npm help json` for definitive documentation on these fields
and exactly what they do.

Use `npm install <pkg>` afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
package name: (rest-api)
version: (1.0.0)
description: A parts catalog
entry point: (index.js)
test command: standard
git repository:
keywords:
author:
license: (ISC)
About to write to /Users/Braden/code/rest-api/package.json:

{
  "name": "rest-api",
  "version": "1.0.0",
  "description": "A parts catalog",
  "main": "index.js",
  "scripts": {
    "test": "standard"
  },
  "author": "",
  "license": "ISC"
}


Is this OK? (yes)


The default entry point is index.js, so you should create a new file by that name. The following code will get you a really basic server that doesn’t really do anything but listens on port 3000 by default.

index.js

const express = require(&apos;express&apos;)
const bodyParser = require(&apos;body-parser&apos;)
const { promisify } = require(&apos;util&apos;)

const app = express()
app.use(bodyParser.json())

const startServer = async () => {
  const port = process.env.SERVER_PORT || 3000
  await promisify(app.listen).bind(app)(port)
  console.log(`Listening on port ${port}`)
}

startServer()


The promisify function of util lets you take a function that expects a callback and instead will return a Promise, which is the new standard as far as handling asynchronous code. This also lets us use the relatively new async/await syntax and make our code look much prettier.

In order for this to work, you need to install the dependencies that you require at the top of the file. Add them using npm install. This will automatically save some metadata to your package.json file and install them locally in a node_modules folder.

Note: You should never commit node_modules to source control because it tends to become bloated quickly, and the package-lock.json file will keep track of the exact versions you used to that if you install this on another machine they get the same code.

$ npm install [email protected] [email protected]


For some quick linting, install standard as a dev dependency, then run it to make sure your code is up to par.

$ npm install --save-dev [email protected]
$ npm test

> [email protected] test /Users/bmk/code/okta/apps/rest-api
> standard


If all is well, you shouldnt see any output past the > standard line. If there’s an error, it might look like this:

$ npm test

> [email protected] test /Users/bmk/code/okta/apps/rest-api
> standard

standard: Use JavaScript Standard Style (https://standardjs.com)
standard: Run `standard --fix` to automatically fix some problems.
  /Users/Braden/code/rest-api/index.js:3:7: Expected consistent spacing
  /Users/Braden/code/rest-api/index.js:3:18: Unexpected trailing comma.
  /Users/Braden/code/rest-api/index.js:3:18: A space is required after &apos;,&apos;.
  /Users/Braden/code/rest-api/index.js:3:38: Extra semicolon.
npm ERR! Test failed.  See above for more details.


Now that your code is ready and you have installed your dependencies, you can run your server with node . (the . says to look at the current directory, and then checks your package.json file to see that the main file to use in this directory is index.js):

$ node .

Listening on port 3000


To test that it’s working, you can use the curl command. There are no endpoints yet, so express will return an error:

$ curl localhost:3000 -i
HTTP/1.1 404 Not Found
X-Powered-By: Express
Content-Security-Policy: default-src &apos;self&apos;
X-Content-Type-Options: nosniff
Content-Type: text/html; charset=utf-8
Content-Length: 139
Date: Thu, 16 Aug 2018 01:34:53 GMT
Connection: keep-alive

<!DOCTYPE html>
<html lang="en">
<head>
<title>{{title}}</title>
</head>
<body>
<pre>Cannot GET /</pre>
</body>
</html>


Even though it says it’s an error, that’s good. You haven’t set up any endpoints yet, so the only thing for Express to return is a 404 error. If your server wasn’t running at all, you’d get an error like this:

$ curl localhost:3000 -i
curl: (7) Failed to connect to localhost port 3000: Connection refused


Build Your REST API with Express, Sequelize, and Epilogue

Now that you have a working Express server, you can add a REST API. This is actually much simpler than you might think. The easiest way I’ve seen is by using Sequelize to define your database schema, and Epilogue to create some REST API endpoints with near-zero boilerplate.

You’ll need to add those dependencies to your project. Sequelize also needs to know how to communicate with the database. For now, use SQLite as it will get us up and running quickly.

npm install [email protected] [email protected] [email protected]


Create a new file database.js with the following code. I’ll explain each part in more detail below.

database.js

const Sequelize = require(&apos;sequelize&apos;)
const epilogue = require(&apos;epilogue&apos;)

const database = new Sequelize({
  dialect: &apos;sqlite&apos;,
  storage: &apos;./test.sqlite&apos;,
  operatorsAliases: false
})

const Part = database.define(&apos;parts&apos;, {
  partNumber: Sequelize.STRING,
  modelNumber: Sequelize.STRING,
  name: Sequelize.STRING,
  description: Sequelize.TEXT
})

const initializeDatabase = async (app) => {
  epilogue.initialize({ app, sequelize: database })

  epilogue.resource({
    model: Part,
    endpoints: [&apos;/parts&apos;, &apos;/parts/:id&apos;]
  })

  await database.sync()
}

module.exports = initializeDatabase


Now you just need to import that file into your main app and run the initialization function. Make the following additions to your index.js file.

index.js

@@ -2,10 +2,14 @@ const express = require(&apos;express&apos;)
 const bodyParser = require(&apos;body-parser&apos;)
 const { promisify } = require(&apos;util&apos;)

+const initializeDatabase = require(&apos;./database&apos;)
+
 const app = express()
 app.use(bodyParser.json())

 const startServer = async () => {
+  await initializeDatabase(app)
+
   const port = process.env.SERVER_PORT || 3000
   await promisify(app.listen).bind(app)(port)
   console.log(`Listening on port ${port}`)


You can now test for syntax errors and run the app if everything seems good:

$ npm test && node .

> [email protected] test /Users/bmk/code/okta/apps/rest-api
> standard

Executing (default): CREATE TABLE IF NOT EXISTS `parts` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `partNumber` VARCHAR(255), `modelNu
mber` VARCHAR(255), `name` VARCHAR(255), `description` TEXT, `createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL);
Executing (default): PRAGMA INDEX_LIST(`parts`)
Listening on port 3000


In another terminal, you can test that this is actually working (to format the JSON response I use a json CLI, installed globally using npm install --global json):

$ curl localhost:3000/parts
[]

$ curl localhost:3000/parts -X POST -d &apos;{
  "partNumber": "abc-123",
  "modelNumber": "xyz-789",
  "name": "Alphabet Soup",
  "description": "Soup with letters and numbers in it"
}&apos; -H &apos;content-type: application/json&apos; -s0 | json
{
  "id": 1,
  "partNumber": "abc-123",
  "modelNumber": "xyz-789",
  "name": "Alphabet Soup",
  "description": "Soup with letters and numbers in it",
  "updatedAt": "2018-08-16T02:22:09.446Z",
  "createdAt": "2018-08-16T02:22:09.446Z"
}

$ curl localhost:3000/parts -s0 | json
[
  {
    "id": 1,
    "partNumber": "abc-123",
    "modelNumber": "xyz-789",
    "name": "Alphabet Soup",
    "description": "Soup with letters and numbers in it",
    "createdAt": "2018-08-16T02:22:09.446Z",
    "updatedAt": "2018-08-16T02:22:09.446Z"
  }
]


What’s Going On Here?

Feel free to skip this section if you followed along with all that, but I did promise an explanation.

The Sequelize function creates a database. This is where you configure details, such as what dialect of SQL to use. For now, use SQLite to get up and running quickly.

const database = new Sequelize({
  dialect: &apos;sqlite&apos;,
  storage: &apos;./test.sqlite&apos;,
  operatorsAliases: false
})


Once you’ve created the database, you can define the schema for it using database.define for each table. Create a table called parts with a few useful fields to keep track of parts. By default, Sequelize also automatically creates and updates id, createdAt, and updatedAt fields when you create or update a row.

const Part = database.define(&apos;parts&apos;, {
  partNumber: Sequelize.STRING,
  modelNumber: Sequelize.STRING,
  name: Sequelize.STRING,
  description: Sequelize.TEXT
})


Epilogue requires access to your Express app in order to add endpoints. However, app is defined in another file. One way to deal with this is to export a function that takes the app and does something with it. In the other file when we import this script, you would run it like initializeDatabase(app).

Epilogue needs to initialize with both the app and the database. You then define which REST endpoints you would like to use. The resource function will include endpoints for the GET, POST, PUT, and DELETE verbs, mostly automagically.

To actually create the database, you need to run database.sync(), which returns a Promise. You’ll want to wait until it’s finished before starting your server.

The module.exports command says that the initializeDatabase function can be imported from another file.

const initializeDatabase = async (app) => {
  epilogue.initialize({ app, sequelize: database })

  epilogue.resource({
    model: Part,
    endpoints: [&apos;/parts&apos;, &apos;/parts/:id&apos;]
  })

  await database.sync()
}

module.exports = initializeDatabase


Secure Your Node + Express REST API with OAuth 2.0

Now that you have a REST API up and running, imagine you’d like a specific application to use this from a remote location. If you host this on the internet as is, then anybody can add, modify, or remove parts at their will.

To avoid this, you can use the OAuth 2.0 Client Credentials Flow. This is a way of letting two servers communicate with each other, without the context of a user. The two servers must agree ahead of time to use a third-party authorization server. Assume there are two servers, A and B, and an authorization server. Server A is hosting the REST API, and Server B would like to access the API.

  • Server B sends a secret key to the authorization server to prove who they are and asks for a temporary token.
  • Server B then consumes the REST API as usual but sends the token along with the request.
  • Server A asks the authorization server for some metadata that can be used to verify tokens.
  • Server A verifies the Server B’s request.* If it’s valid, a successful response is sent and Server B is happy. * If the token is invalid, an error message is sent instead, and no sensitive information is leaked.
Create an Authorization Server

This is where Okta comes into play. Okta can act as an authorization server to allow you to secure your data.

You’re probably asking yourself “Why Okta? Well, it’s pretty cool to build a REST app, but it’s even cooler to build a secure one. To achieve that, you’ll want to add authentication so users have to log in before viewing/modifying groups. At Okta, our goal is to make identity management a lot easier, more secure, and more scalable than what you’re used to. Okta is a cloud service that allows developers to create, edit, and securely store user accounts and user account data, and connect them with one or multiple applications.

After creating your account, log in to your developer console, navigate to API, then to the Authorization Servers tab. Click on the link to your default server.

From this Settings tab, copy the Issuer field. You’ll need to save this somewhere that your Node app can read. In your project, create a file named .env that looks like this:

.env

ISSUER=https://{yourOktaDomain}/oauth2/default


The value for ISSUER should be the value from the Settings page’s Issuer URI field.

Note: As a general rule, you should not store this .env file in source control. This allows multiple projects to use the same source code without needing a separate fork. It also makes sure that your secure information is not public (especially if you’re publishing your code as open source).

Next, navigate to the Scopes tab. Click the Add Scope button and create a scope for your REST API. You’ll need to give it a name (e.g. parts_manager) and you can give it a description if you like.

You should add the scope name to your .env file as well so your code can access it.

.env

ISSUER=https://{yourOktaDomain}/oauth2/default
SCOPE=parts_manager


Now you need to create a client. Navigate to Applications, then click Add Application. Select Service, then click Next. Enter a name for your service, (e.g. Parts Manager), then click Done.

This will take you to a page that has your client credentials. These are the credentials that Server B (the one that will consume the REST API) will need in order to authenticate. For this example, the client and server code will be in the same repository, so go ahead and add this data to your .env file. Make sure to replace {yourClientId} and {yourClientSecret} with the values from this page.

CLIENT_ID={yourClientId}
CLIENT_SECRET={yourClientSecret}


Create Middleware to Verify Tokens in Express

In Express, you can add middleware that will run before each endpoint. You can then add metadata, set headers, log some information, or even cancel the request early and send an error message. In this case, you’ll want to create some middleware that verifies the token sent by the client. If the token is valid, it will continue to the REST API and return the appropriate response. If the token is invalid, it will instead respond with an error message so that only authorized machines have access.

To validate tokens, you can use Okta’s middleware. You’ll also need a tool called dotenv to load the environment variables:

npm install [email protected] @okta/[email protected]


Now create a file named auth.js that will export the middleware:

auth.js

const OktaJwtVerifier = require(&apos;@okta/jwt-verifier&apos;)

const oktaJwtVerifier = new OktaJwtVerifier({ issuer: process.env.ISSUER })

module.exports = async (req, res, next) => {
  try {
    const { authorization } = req.headers
    if (!authorization) throw new Error(&apos;You must send an Authorization header&apos;)

    const [authType, token] = authorization.trim().split(&apos; &apos;)
    if (authType !== &apos;Bearer&apos;) throw new Error(&apos;Expected a Bearer token&apos;)

    const { claims } = await oktaJwtVerifier.verifyAccessToken(token)
    if (!claims.scp.includes(process.env.SCOPE)) {
      throw new Error(&apos;Could not verify the proper scope&apos;)
    }
    next()
  } catch (error) {
    next(error.message)
  }
}


This function first checks that the authorization header is on the request and throws an error otherwise. If it exists, it should look like Bearer {token} where {token} is a JWT string. This will throw another error if the header doesn’t start with Bearer. Then we send the token to Okta’s JWT Verifier to validate the token. If the token is invalid, the JWT verifier will throw an error. Otherwise, it will return an object with some information. You can then verify that the claims include the scope that you’re expecting.

If everything is successful, it calls the next() function without any parameters, which tells Express that it’s OK to move on to the next function in the chain (either another middleware or the final endpoint). If you pass a string into the next function, Express treats it as an error that will be passed back to the client, and will not proceed in the chain.

You still need to import this function and add it as middleware to your app. You also need to load dotenv at the top of your index file to make sure that the environment variables from .env are loaded in your app. Make the following changes to index.js:

index.js

@@ -1,11 +1,14 @@
+require(&apos;dotenv&apos;).config()
 const express = require(&apos;express&apos;)
 const bodyParser = require(&apos;body-parser&apos;)
 const { promisify } = require(&apos;util&apos;)

+const authMiddleware = require(&apos;./auth&apos;)
 const initializeDatabase = require(&apos;./database&apos;)

 const app = express()
 app.use(bodyParser.json())
+app.use(authMiddleware)

 const startServer = async () => {
   await initializeDatabase(app)


To test that requests are properly blocked, try running it again…

$ npm test && node .


…then in another terminal run a few curl commands to test for:

  1. An authorization header is required
$ curl localhost:3000/parts
<!DOCTYPE html>






<html lang="en">
<head>


</head>
<body>
<pre>You must send an Authorization header</pre>
</body>
</html>


  1. An authorization header is required
$ curl localhost:3000/parts -H &apos;Authorization: Basic asdf:1234&apos;
<!DOCTYPE html>
<html lang="en">
<head>


</head>
<body>
<pre>Expected a Bearer token</pre>
</body>
</html>


  1. An authorization header is required
$ curl localhost:3000/parts -H &apos;Authorization: Bearer asdf&apos;
<!DOCTYPE html>
<html lang="en">
<head>
</head>
<body>
<pre>Jwt cannot be parsed</pre>
</body>
</html>


Create a Test Client in Node

You have now disabled access to the app for someone without a valid token, but how do you get a token and use it? I’ll show you how to write a simple client in Node, which will also help you test that a valid token works.

npm install [email protected] [email protected]


client.js

require(&apos;dotenv&apos;).config()
const request = require(&apos;request-promise&apos;)
const btoa = require(&apos;btoa&apos;)

const { ISSUER, CLIENT_ID, CLIENT_SECRET, SCOPE } = process.env

const [,, uri, method, body] = process.argv
if (!uri) {
  console.log(&apos;Usage: node client {url} [{method}] [{jsonData}]&apos;)
  process.exit(1)
}

const sendAPIRequest = async () => {
  const token = btoa(`${CLIENT_ID}:${CLIENT_SECRET}`)
  try {
    const auth = await request({
      uri: `${ISSUER}/v1/token`,
      json: true,
      method: &apos;POST&apos;,
      headers: {
        authorization: `Basic ${token}`
      },
      form: {
        grant_type: &apos;client_credentials&apos;,
        scope: SCOPE
      }
    })

    const response = await request({
      uri,
      method,
      body,
      headers: {
        authorization: `${auth.token_type} ${auth.access_token}`
      }
    })

    console.log(response)
  } catch (error) {
    console.log(`Error: ${error.message}`)
  }
}

sendAPIRequest()


Here the code is loading the variables from .env into the environment, then grabbing them from Node. Node stores environment variables in process.env (process is a global variable with a bunch of useful variables and functions).

require(&apos;dotenv&apos;).config()
// ...
const { ISSUER, CLIENT_ID, CLIENT_SECRET, SCOPE } = process.env
// ...


Next, since this will be run from the command line, you can use process again to grab the arguments passed in with process.argv. This gives you an array with all the arguments passed in. The first two commas are there without variable names in front of them because the first two are unimportant in this case; those will just be the path to node, and the name of the script (client or client.js).

The URL is required, which would include the endpoint, but the method and JSON data are optional. The default method is GET, so if you’re just fetching data you can leave that out. You also wouldn’t need any payload in that case. If the arguments don’t seem right, then this will exit the program with an error message and an exit code of 1, signifying an error.

const [,, uri, method, body] = process.argv
if (!uri) {
  console.log(&apos;Usage: node client {url} [{method}] [{jsonData}]&apos;)
  process.exit(1)
}


Node currently doesn’t allow for await in the main thread, so to make use of the cleaner async/await syntax, you have to create a function and then call it afterward.

If an error occurs in any of the awaited functions, the try/catch they’ll be printed out to the screen.

const sendAPIRequest = async () => {
  try {
    // ...
  } catch (error) {
    console.error(`Error: ${error.message}`)
  }
}

sendAPIRequest()


This is where the client sends a request to the authorization server for a token. For authorizing with the authorization server itself, you need to use Basic Auth. Basic Auth is the same thing a browser uses when you get one of those built-in pop-ups asking for a username and password. Say your username is AzureDiamond and your password is hunter2. Your browser would then concatenate them together with a colon (:) and then encode them with base64 (this is what the btoa function does) to get QXp1cmVEaWFtb25kOmh1bnRlcjI=. It then sends an authorization header of Basic QXp1cmVEaWFtb25kOmh1bnRlcjI=. The server can then decode the token with base64 to get the username and password.

Basic authorization isn’t inherently secure because it’s so easy to decode, which is why https is important, to prevent a man-in-the-middle attack. Here, the client ID and client secret are the username and password, respectively. That’s also why it’s important to keep your CLIENT_ID and CLIENT_SECRET private.

For OAuth 2.0, you also need to specify the grant type, which in this case is client_credentials since you’re planning to talk between two machines. You also need to specify the scope. There are a lot of other options that could be added here, but this is all we need for this demo.

const token = btoa(`${CLIENT_ID}:${CLIENT_SECRET}`)
const auth = await request({
  uri: `${ISSUER}/v1/token`,
  json: true,
  method: &apos;POST&apos;,
  headers: {
    authorization: `Basic ${token}`
  },
  form: {
    grant_type: &apos;client_credentials&apos;,
    scope: SCOPE
  }
})


Once you’re authenticated, you’ll get an access token that you can send along to your REST API that should look something like Bearer eyJra...HboUg (the actual token is much longer than that – likely somewhere around 800 characters). The token contains all the information needed for the REST API to verify who you are, when the token will expire, and all kinds of other information, like the scopes requested, the issuer, and the client ID used to request the token.

The response from the REST API is then printed to the screen.

const response = await request({
  uri,
  method,
  body,
  headers: {
    authorization: `${auth.token_type} ${auth.access_token}`
  }
})

console.log(response)


Go ahead and test it out now. Again, start the app with npm test && node ., then try out some commands like the following:

$ node client http://localhost:3000/parts | json
[
  {
    "id": 1,
    "partNumber": "abc-123",
    "modelNumber": "xyz-789",
    "name": "Alphabet Soup",
    "description": "Soup with letters and numbers in it",
    "createdAt": "2018-08-16T02:22:09.446Z",
    "updatedAt": "2018-08-16T02:22:09.446Z"
  }
]

$ node client http://localhost:3000/parts post &apos;{
  "partNumber": "ban-bd",
  "modelNumber": 1,
  "name": "Banana Bread",
  "description": "Bread made from bananas"
}&apos; | json
{
  "id": 2,
  "partNumber": "ban-bd",
  "modelNumber": "1",
  "name": "Banana Bread",
  "description": "Bread made from bananas",
  "updatedAt": "2018-08-17T00:23:23.341Z",
  "createdAt": "2018-08-17T00:23:23.341Z"
}

$ node client http://localhost:3000/parts | json
[
  {
    "id": 1,
    "partNumber": "abc-123",
    "modelNumber": "xyz-789",
    "name": "Alphabet Soup",
    "description": "Soup with letters and numbers in it",
    "createdAt": "2018-08-16T02:22:09.446Z",
    "updatedAt": "2018-08-16T02:22:09.446Z"
  },
  {
    "id": 2,
    "partNumber": "ban-bd",
    "modelNumber": "1",
    "name": "Banana Bread",
    "description": "Bread made from bananas",
    "createdAt": "2018-08-17T00:23:23.341Z",
    "updatedAt": "2018-08-17T00:23:23.341Z"
  }
]

$ node client http://localhost:3000/parts/1 delete | json
{}

$ node client http://localhost:3000/parts | json
[
  {
    "id": 2,
    "partNumber": "ban-bd",
    "modelNumber": "1",
    "name": "Banana Bread",
    "description": "Bread made from bananas",
    "createdAt": "2018-08-17T00:23:23.341Z",
    "updatedAt": "2018-08-17T00:23:23.341Z"
  }
]


Learn More

Building A REST API With MongoDB, Mongoose, And Node.js

Creating RESTful APIs with NodeJS and MongoDB Tutorial

How to build RESTful APIs with ASP.NET Core

Understanding the basics of RESTful APIs

Build a CRUD App with Angular and Firebase

The Complete Node.js Developer Course (2nd Edition)

Learn and Understand NodeJS

Angular (Angular 2+) & NodeJS - The MEAN Stack Guide

Beginner Full Stack Web Development: HTML, CSS, React & Node

Creating a RESTful Web API with Node.js and Express.js from scratch

Creating a RESTful Web API with Node.js and Express.js from scratch

In this article, I’ll show you step by step how to create a RESTful Web API with Node.js and Express.js by building a simple and useful Todo API. This article assumes you have basic javascript knowledge and terminal using capabilities.

In this article, I’ll show you step by step how to create a RESTful Web API with Node.js and Express.js by building a simple and useful Todo API. This article assumes you have basic javascript knowledge and terminal using capabilities.

You can also build a Web API in Node.js by using another framework except Express.js but Express.js is one of the most popular web framework for Node.js.

You can found the final source code of this Web API in this github repository.

Let’s start to create our mentioned Web API.

Before start

If you have never used Node.js or npm package manager you should install them.

To check whether the Node.js is already installed on your computer, open your terminal and run node -v command. If you see your Node.js version it's installed. Otherwise go to below link.

Click here to download and install Node.js (You can choose LTS version)

And if you don’t have any IDE or text editor for writing javascript I advice you Visual Studio Code.

Click here to download VS Code (Optional)

About express-generator

In fact we could use <a href="https://expressjs.com/en/starter/generator.html" target="_blank">express-generator</a> tool which designed to creating an Express Web API quickly but I want to create this API from scratch because of that tool puts some extra files and folder structures that we don't need them now. But you can use this useful tool next time on creating new Web API. I won't use it now due to keep article simple.

Creating Project

Go to your workspace root folder and create a new folder there named "todo-api".

Then create "package.json" and "server.js" files into "todo-api" folder like below.

package.json

{
    "name": "todo-api",
    "version": "1.0.0",
    "scripts": {
        "start": "node server.js"
    },
    "dependencies": {
        "express": "^4.16.4"
    }
}

server.js

const http = require('http');
const express = require('express');
const app = express();
app.use(express.json());
app.use('/', function(req, res) {
    res.send('todo api works');
});
const server = http.createServer(app);
const port = 3000;
server.listen(port);
console.debug('Server listening on port ' + port);

After creating above files open your terminal in the "todo-api" folder and run npm installcommand.

This command will be install your project dependencies which pointed at the "package.json" file.

After finished package download process, downloaded dependency files will be installed into"node_modules" folder at the root of the "todo-api" folder.

After finished package installing then run npm start to start our Web API.

Now our Web API listening. To see result open your web browser then write localhost:3000 to address bar and press enter.

As result you’ll see our request handler response in your browser: “todo api works”.

This is a dead simple Express.js Web API. And it needs the some development. For example we need to an api endpoint to get todo items. So let’s add a new API endpoint for this.

Create a new folder named "routes" in the root of the "todo-api" folder.

Then create a "items.js" file inside of "routes" folder and put following codes inside it.

Your final folder structure should be like below;

/todo-api
/node_modules
/routes
    items.js
package.json
server.js

items.js

const express = require('express');
const router = express.Router();
const data = [
    {id: 1, title: 'Finalize project', order: 1, completed: false, createdOn: new Date()},
    {id: 2, title: 'Book ticket to London', order: 2, completed: false, createdOn: new Date()},
    {id: 3, title: 'Finish last article', order: 3, completed: false, createdOn: new Date()},
    {id: 4, title: 'Get a new t-shirt', order: 4, completed: false, createdOn: new Date()},
    {id: 5, title: 'Create dinner reservation', order: 5, completed: false, createdOn: new Date()},
];
router.get('/', function (req, res) {
    res.status(200).json(data);
});
router.get('/:id', function (req, res) {
    let found = data.find(function (item) {
        return item.id === parseInt(req.params.id);
    });
    if (found) {
        res.status(200).json(found);
    } else {
        res.sendStatus(404);
    }
});
module.exports = router;

Initial code of "items.js" file contains two endpoints. First one gets all todo items and second one gets one item which matches given id parameter.

Before testing items routes we should register it in the "server.js" file.

Modify "server.js" file like below to register new item routes.

server.js

const http = require('http');
const express = require('express');
const itemsRouter = require('./routes/items');
const app = express();
app.use(express.json());
app.use('/items', itemsRouter);
app.use('/', function(req, res) {
    res.send('todo api works');
});
const server = http.createServer(app);
const port = 3000;
server.listen(port);
console.debug('Server listening on port ' + port);

Now run npm start to start our Web API.

Then open your web browser and write localhost:3000/items to address bar and press enter.

You’ll see todo items json array in the response body.

And write localhost:3000/items/3 to address bar and press enter.

You’ll see the todo item which has id 3 in the response body.

But not finished up yet.

CRUD Operations and HTTP methods

I think we’ll need CRUD operations to Create, Read, Update and Delete todo items.

We have already two endpoints for getting items. So we need Create, Update and Delete endpoints.

Let’s add also these endpoints into the items.js file.

Our final "items.js" file and endpoints should be like below.

const express = require('express');
const router = express.Router();

const data = [
  {id: 1, title: 'Finalize project',          order: 1, completed: false, createdOn: new Date()},
  {id: 2, title: 'Book ticket to London',     order: 2, completed: false, createdOn: new Date()},
  {id: 3, title: 'Finish last article',       order: 3, completed: false, createdOn: new Date()},
  {id: 4, title: 'Get a new t-shirt',         order: 4, completed: false, createdOn: new Date()},
  {id: 5, title: 'Create dinner reservation', order: 5, completed: false, createdOn: new Date()},
];

router.get('/', function (req, res) {
  res.status(200).json(data);
});

router.get('/:id', function (req, res) {
  let found = data.find(function (item) {
    return item.id === parseInt(req.params.id);
  });

  if (found) {
    res.status(200).json(found);
  } else {
    res.sendStatus(404);
  }
});

router.post('/', function (req, res) {
  let itemIds = data.map(item => item.id);
  let orderNums = data.map(item => item.order);

  let newId = itemIds.length > 0 ? Math.max.apply(Math, itemIds) + 1 : 1;
  let newOrderNum = orderNums.length > 0 ? Math.max.apply(Math, orderNums) + 1 : 1;

  let newItem = {
    id: newId,
    title: req.body.title,
    order: newOrderNum,
    completed: false,
    createdOn: new Date()
  };

  data.push(newItem);

  res.status(201).json(newItem);
});

router.put('/:id', function (req, res) {
  let found = data.find(function (item) {
    return item.id === parseInt(req.params.id);
  });

  if (found) {
    let updated = {
      id: found.id,
      title: req.body.title,
      order: req.body.order,
      completed: req.body.completed
    };

    let targetIndex = data.indexOf(found);

    data.splice(targetIndex, 1, updated);

    res.sendStatus(204);
  } else {
    res.sendStatus(404);
  }
});

router.delete('/:id', function (req, res) {
  let found = data.find(function (item) {
    return item.id === parseInt(req.params.id);
  });

  if (found) {
    let targetIndex = data.indexOf(found);

    data.splice(targetIndex, 1);
  }

  res.sendStatus(204);
});

module.exports = router;

Short Explanation

I wanna explain shortly some points of our last codes.

First of all you must have noticed that our api works on a static data and keeps it on memory. All of our GET, POST, PUT and DELETE http methods just manipulate a json array. The purpose of this is to keep article simple and draw attention to the Web API structure.

Due to this situation our POST method has some extra logic such as calculating next item ids and order numbers.

So you can modify logic and data structures in these http methods to use a database or whatever you want.

Testing API with Postman

We have tested the GET methods of our Web API in our web browser and seen responses. But we can’t test directly POST, PUT and DELETE http methods in web browser.

If you want to test also other http methods you should use Postman or another http utility.

Now I’ll show you how to test the Web API with Postman

Before we start click here and install Postman.

When you first launch Postman after installing you’ll see start window. Close this start window by clicking close button on top right corner. Then you must see following screen.

An empty Postman request

Sending GET Request

Before sending a request to API we should start it by running npm startcommand as we do before.

After start the Web API and seeing “Server listening on…” message write localhost:3000/itemsto address bar as seen below and click Send button. You'll see todo items array as API response like below.

Sending a GET request with Postman

You can try similarly by giving an item id in request url like this localhost:3000/items/3

Sending POST Request

To sending a POST request and create a new todo item write localhost:3000/items to address bar and change HTTP verb to POST by clicking arrow at front of the address bar as seen below.

Sending a POST request with Postman

Before sending the POST request you should add request data to body of the request by clicking body tab and selecting raw and JSON as seen below.

Attaching a JSON body to POST request in Postman

Now click Send button to send POST request to the Web API. Then you must get “201 Created” http response code and seeing created item in the response body.

To see the last status of todo items send a get request to localhost:3000/itemsaddress. You must see newly created item at the end of the list.

Sending PUT Request

Sending PUT request is very similar to sending POST request.

The most obvious difference is request url should be pointed specific item like this localhost:3000/items/3

And you should choose PUT as http verb instead of POST and send all of the required data in the request body unlike POST.

For example you could send a JSON body in the PUT request as below.

An example JSON body for PUT request

{
    "title": "New title of todo item",
    "order": 3,
    "completed": false
}

When you click Send button you must get “204 No Content” http response code. You can check item you updated by sending a get request.

Sending DELETE Request

To send a DELETE request, change the request url to address a specific item id like this localhost:3000/items/3

And select DELETE as http verb and click Send button.

You must get “204 No Content” http response code as result of the DELETE operation.

Send a get request and see the last status of list.

About the DELETE Http Request

I want to say a few words about DELETE http request. You must have noticed something in our delete code. DELETE request returns “204 No Content” every situation.

Http DELETE requests are idempotent. So what that mean? If you delete a resource on server by sending DELETE request, it’s removed from the collection. And every next DELETE request on the same resource won’t change outcome. So you won’t get “404 Not Found” in the second request. Each request returns same response whether succeed or not. That’s mean idempotent operation.

Conclusion

Finally we’ve tested all http methods of our Web API.

As you can see, it works just fine.

Thanks for reading ❤

If you liked this post, share it with all of your programming buddies!

How to create a REST API with Node.js

How to create a REST API with Node.js

In this article, you'll learn the process of building REST API with Node.js from scratch.

What is REST API?

REST or RESTful stands for REpresentational State Transfer. It is an architectural style as well as an approach for communications purpose that is often used in various web services development. In simpler terms, it is an application program interface (API) which makes use of the HTTP requests to GET, PUT, POST and DELETE the data over WWW.

REST architectural style helps in leveraging the lesser use of bandwidth which makes an application more suitable for the internet. It is often regarded as the “language of the internet”. It is completely based on the resources where each and every component is regarded as a component and a single resource is accessible through a common interface using the standard HTTP method.

To understand better, let’s dive a little deeper and see how exactly does a REST API work. Basically, the REST API breaks down a transaction in order to create small modules. Now, each of these modules is used to address a specific part of the transaction. This approach provides more flexibility but requires a lot of effort to be built from the very scratch.

The main functions used in any REST based architecture are:

  • GET − Provides read-only access to a resource.
  • PUT − Creates a new resource.
  • DELETE − Removes a resource.
  • POST − Updates an existing resource or creates a new resource.

But all who claims cannot be referred to as RESTful API. In order to be regarded as a RESTful API, your application must satisfy certain constraints or principles. In the next section of this article on Building a REST API using Node.js, I will be talking about these principles in detail.

Principles of REST

Well, there are six ground principles laid down by Dr. Fielding who was the one to define the REST API design in 2000. Below are the six guiding principles of REST:

  1. Stateless
  2. Requests sent from a client to the server contains all the necessary information that is required to completely understand it. It can be a part of the URI, query-string parameters, body, or even headers. The URI is used for uniquely identifying the resource and the body holds the state of the requesting resource. Once the processing is done by the server, an appropriate response is sent back to the client through headers, status or response body.
  3. Client-Server
  4. It has a uniform interface that separates the clients from the servers. Separating the concerns helps in improving the user interface’s portability across multiple platforms as well as enhance the scalability of the server components.
  5. Uniform Interface
  • To obtain the uniformity throughout the application, REST has defined four interface constraints which are:Resource identification
  • Resource Manipulation using representations
  • Self-descriptive messages
  • Hypermedia as the engine of application state
  1. Cacheable
  2. In order to provide a better performance, the applications are often made cacheable. It is done by labeling the response from the server as cacheable or non-cacheable either implicitly or explicitly. If the response is defined as cacheable, then the client cache can reuse the response data for equivalent responses in the future. It also helps in preventing the reuse of the stale data.
  3. Layered system
  4. The layered system architecture allows an application to be more stable by limiting component behavior. This architecture enables load balancing and provides shared caches for promoting scalability. The layered architecture also helps in enhancing the application’s security as components in each layer cannot interact beyond the next immediate layer they are in.
  5. Code on demand
  6. Code on Demand is an optional constraint and is used the least. It permits a clients code or applets to be downloaded and extended via the interface to be used within the application. In essence, it simplifies the clients by creating a smart application which doesn’t rely on its own code structure.

Now that you know what is a REST API and what all you need to mind in order to deliver an efficient application, let’s dive deeper and see the process of building REST API using Node.js.

Practical Demonstration: Building REST API using Node.js

Here, we will be creating a simple CRUD REST application for Library Management using Node.js and Express.js. To build this application, you will need to install the following:

  1. Node.js
  2. Express.js
  3. Joi
  4. nodemon (Node Monitor)

In this example, I will be using the WebStorm IDE to write and execute the codes. You can use any IDE or code editor according to your choice. So, let’s get started.

First, you need to create your project directory. Next, open the command prompt and navigate to your project directory. Once there, you need to call npm using the below command:

npm init

When you hit enter, Node.js will ask you to enter some details to build the .json file such as:

Here you can define your entry point along with several other information. For this demo, I will be using script.js as an entry point. Next, we will be installing Express.js using the below command:

npm i express

Finally, I will be installing a node monitoring package called nodemon. It keeps a watch on all the files with any type of extension present in this folder. Also, with nodemon on the watch, you don’t have to restart the Node.js server each time any changes are made. nodemon will implicitly detect the changes and restart the server for you.

npm i -g nodemon

package.json

{
"name": "samplerestapi",
"version": "1.0.0",
"description": "Edureka REST API with Node.js",
"main": "script.js",
"scripts": {
"test": "echo "Error: no test specified" && exit 1"
},
"author": "Edureka",
"license": "ISC",
"dependencies": {
"express": "^4.16.4",
"joi": "^13.1.0"
}
}

script.js

const express = require('express');
const Joi = require('joi'); //used for validation
const app = express();
app.use(express.json());

const books = [
{title: 'Harry Potter', id: 1},
{title: 'Twilight', id: 2},
{title: 'Lorien Legacies', id: 3}
]

//READ Request Handlers
app.get('/', (req, res) => {
res.send('Welcome to Edurekas REST API with Node.js Tutorial!!');
});

app.get('/api/books', (req,res)=> {
res.send(books);
});

app.get('/api/books/:id', (req, res) => {
const book = books.find(c => c.id === parseInt(req.params.id));

if (!book) res.status(404).send('<h2 style="font-family: Malgun Gothic; color: darkred;">Ooops... Cant find what you are looking for!</h2>');
res.send(book);
});

//CREATE Request Handler
app.post('/api/books', (req, res)=> {

const { error } = validateBook(req.body);
if (error){
res.status(400).send(error.details[0].message)
return;
}
const book = {
id: books.length + 1,
title: req.body.title
};
books.push(book);
res.send(book);
});

//UPDATE Request Handler
app.put('/api/books/:id', (req, res) => {
const book = books.find(c=> c.id === parseInt(req.params.id));
if (!book) res.status(404).send('<h2 style="font-family: Malgun Gothic; color: darkred;">Not Found!! </h2>');

const { error } = validateBook(req.body);
if (error){
res.status(400).send(error.details[0].message);
return;
}

book.title = req.body.title;
res.send(book);
});

//DELETE Request Handler
app.delete('/api/books/:id', (req, res) => {

const book = books.find( c=> c.id === parseInt(req.params.id));
if(!book) res.status(404).send('<h2 style="font-family: Malgun Gothic; color: darkred;"> Not Found!! </h2>');

const index = books.indexOf(book);
books.splice(index,1);

res.send(book);
});

function validateBook(book) {
const schema = {
title: Joi.string().min(3).required()
};
return Joi.validate(book, schema);

}

//PORT ENVIRONMENT VARIABLE
const port = process.env.PORT || 8080;
app.listen(port, () => console.log(`Listening on port ${port}..`));

Now, the next step is to check whether the handlers are working properly or not. For that, we will use a Chrome extension called Postman. To install Postman you can visit hereand click on ‘Add to Chrome’.

Once you have successfully installed Postman, open it and start testing your application. So, let’s start off by testing our GET method. Now, in order to do that you need to select GET from the drop-down list, type in the defined URI and hit send. If your code is working fine, then you will see the list of all the books which we have added manually in our code. In the below picture, you can see how my result looks like.

Now, let’s try adding a new book to our inventory list. For that, select ‘POST’ from the drop-down list and type in the defined URI for the POST method. Now, click on ‘Body’, select ‘raw’ and move on to select ‘JSON’ from the drop-down list as depicted in the below image. Now, in the text area, type in the title of your book as shown and hit send.

If your POST method is working fine, your response body will contain the book title along with the book id. Now, let’s try to update the book title. Currently, my book title is “Angels and Demons” which I will be updating to “Angels & Demons”. So, to update the data, you need to first select ‘PUT’ from the drop-down table and enter the PUT request’s URI along with the book id you wish to update. Next in the ‘Body’, type in the new book title and hit enter.


This will give you a response with the book id and updated book title.

Finally, let’s send a ‘DELETE’ request to delete an existing record. For that select DELETE from the drop-down list and type in the URI of the delete request handler along with the book details, you want to remove and hit enter. If your transaction is successful, you will see the complete details of the entry you have removed in the response body.

Now, let’s send a GET request for our final list of books.


As you can see from the above screenshot, the response body contains a total of three books with the book id 3 missing as we have already deleted that entry.