Setting up a REST-API using Express
Tags: javascript, node, tutorial, express
Published: 2020-09-27 15:03:25

In this article, I want to show you a quick and solid way to set up a [REST-API](https://en.wikipedia.org/wiki/Representational_state_transfer) with [ExpressJS](http://expressjs.com/). This does not include any kind of authentication or authorization. We will just be setting up one single route and define some standards that will help us expand this API if we chose to do so.

### Prerequisites
Before we can start coding we need to get a version of NodeJS and NPM installed on our system. Just head over to the official website linked [here](https://nodejs.org/en/) and download the LTS (long time support) version of NodeJS. This will automatically install [NPM](https://www.npmjs.com/) alongside it as its package manager.

Next, we generate our project by creating a folder called *express_api_template* and then using *npm* to initialize a NodeJS project.
```Shell
$ mkdir express_api_template
$ cd express_api_template/
$ npm init
```
*npm, init* will walk you through a process for setting up a new project. I usually use the default settings except for *entry point*. I like to call my main javascript file *server.js* instead of the default *index.js* and then fill out the author.
After this is done we need to install ExpressJS by adding it to our *package.json*. We will use the following command for this.
```Shell
$ npm install express
```
After it is finished downloading we should have a *node_modules* folder and the two files *package.json* and *package-lock.json*.

### The Basics
First, we need to create two new folders and two new files, and some new dependencies.
```Shell
$ mkdir src
$ mkdir src/config
$ touch src/server.js src/config/config.env
$ npm install colors dotenv
$ npm install nodemon --save-dev
```
The difference between the plain install and the --save-dev install is that the plain one installs dependencies needed for production. --save-dev installs dependencies only needed for development.
But what did we actually install here?
- ***[colors](https://www.npmjs.com/package/colors):*** This package is used to make the console outputs colorful.
- ***[dotenv](https://www.npmjs.com/package/dotenv):*** This package loads environment variables from .env files into process.env.{variable_name}
- ***[nodemon](https://nodemon.io/):*** This is used in development for reloading your server every time you save changes.

Installing all this won't make the application run. For that we need to do two more things:
1. Configuring our *package.json* to start *server.js*
2. Implementing a basic Express server in *server.js*

Let's start by configuring *package.json* like this:
```JSON
{
  "name": "express_api_template",
  "version": "1.0.0",
  "description": "",
  "main": "src/server.js",
  "scripts": {
    "start": "NODE_ENV=production node src/server.js",
    "dev": "NODE_ENV=development nodemon src/server.js"
  },
  "author": "Jakob Klamser",
  "license": "ISC",
  "dependencies": {
    "colors": "^1.4.0",
    "dotenv": "^8.2.0",
    "express": "^4.17.1"
  },
  "devDependencies": {
    "nodemon": "^2.0.4"
  }
}
```
We defined two commands to use with **npm**.
The first one is for production. It sets the NODE_ENV variable to *production* and then starts the server by using the *node-command*.
The second one is for development and is setting the NODE_ENV to *development* and then using *nodemon* instead of *node* so that we can make use of the reload-on-save functionality while we are in development.

**NOTE:** If you are using Windows as an operating system you need to install  [cross-env](https://www.npmjs.com/package/cross-env) as a development dependency to set the NODE_ENV.
```Shell
$ npm install cross-env --save-dev
```
And then edit the two scripts like this:
```JSON
"scripts": {
  "start": "cross-env NODE_ENV=production node src/server.js",
  "dev": "cross-env NODE_ENV=development nodemon src/server.js"
},
```

For all of this to work, we need to finish up step two first. We have to create an express application and then start it by using a port that we define in our *config.env*.
Add the port to the file like this:
```Shell
PORT=5000
```
Now we can go ahead and begin with writing some code on *server.js*.
```JavaScript
const express = require('express');
const dotenv = require('dotenv');
const colors = require('colors');

dotenv.config({ path: 'src/config/config.env' });

const app = express();

const PORT = process.env.PORT || 5001;

app.listen(PORT,
  console.log(`Server up and running in ${process.env.NODE_ENV} mode on port ${PORT}`.yellow.bold));
```
This is really straight forward, we set the path to our *config.env* and then initialize the express app. After that, we start listening at the port we just set in our *config.env*.
If we run the following command:
```Shell
$ npm run dev
```
You should see the following output:
```Shell
[nodemon] 2.0.4
[nodemon] to restart at any time, enter `rs`
[nodemon] watching path(s): *.*
[nodemon] watching extensions: js,mjs,json
[nodemon] starting `node src/server.js`
Server up and running in development mode on port 5000
```
Now we got a really basic setup going. Now let's make this a template for a REST-API in the next section.

### Debug and Security
Our goal is to add some more features like logging more information to the console for debugging purposes and securing the API. 

To achieve the aforementioned goals we need to add a few more dependencies.
```Shell
$ npm install helmet cors
$ npm install morgan  --save-dev
```
What do these three new dependencies do?
- ***[helmet](https://helmetjs.github.io/):*** This package is a middleware that helps to secure your API by adding multiple HTTP headers.
- ***[cors](https://www.npmjs.com/package/cors):*** This middleware helps us implementing [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS). 
- ***[morgan](https://www.npmjs.com/package/morgan):*** This is a simple HTTP request logger, it outputs the incoming requests to the node console.

After installing all these middlewares we need to go ahead and add them to our express application.
```JavaScript
// After the other require statements:
const helmet = require('helmet');
const cors = require('cors');

// Between const app and const PORT:
// Development Setup
if (process.env.NODE_ENV === 'development') {
  // require morgan if in development mode
  // setting morgan to dev: https://www.npmjs.com/package/morgan#dev
  app.use(require('morgan')('dev'));
}

// Put all the server-wide middleware here
app.use(cors({
  origin: process.env.CORS_ORIGIN,
}));
app.use(helmet());
```
Most notably is the new check on the current NODE_ENV. We do that because we only require *morgan* if we are in development mode. If you later want to add in something like a data seeding script for a database in development then you can do it right there.

After that new check, we connect the middlewares to our express application. For *cors*, we configure an origin. This means only requests from this origin are allowed to communicate with our API. For example a frontend application you built. We just need to add the address to our *config.env* file like this:
```Shell
CORS_ORIGIN=http://localhost:8000
```
The port might be different depending on your web applications development set up. If that's the case just go ahead and change it.

### Endpoint and Custom Middleware
Now that we are done securing the API we will implement two basic middlewares and one example route. To keep the project clean and maintainable we will add three more folders and four new files.
```Shell
$ mkdir src/routes src/middleware src/controllers
$ touch src/middleware/notFound.js src/middleware/errorHandler.js src/routes/post.js src/controllers/postsController.js
```

#### Middleware
We start by creating our first middleware function in *notFound.js* that handles requests that don't hit an API endpoint by throwing a *404 Not Found Error*.
```JavaScript
const notFound = (req, res, next) => {
  const error = new Error(`Not Found - ${req.originalUrl}`);
  res.status(404);
  next(error);
};

module.exports = notFound;
```
It is simply a function that takes in the request, response, and next. We create an error and set the HTTP status code to 404 and pass in the error to next. 

This middleware alone won't help us at all. We need something to handle incoming errors, such as the *Not Found Error* we just created. For that, we implement our next middleware function called *errorHandler*.
```JavaScript
const errorHandler = (error, req, res, next) => {
  const statusCode = res.statusCode === 200 ? 500 : res.statusCode;
  res.status(statusCode);
  res.json({
    message: error.message,
    stack: process.env.NODE_ENV === 'production' ? ':(' : error.stack,
  });
};

module.exports = errorHandler;
```
If someone hits a route that has no endpoint our API will return a JSON object that contains the error message and if we are running in development it also returns the stack.

The last step is to add the middlewares to our *server.js*.
```JavaScript
// After the other require statements:
const notFound = require('./middleware/notFound');
const errorHandler = require('./middleware/errorHandler');
// Custom middleware here
app.use(notFound);
app.use(errorHandler);
```

#### Router
We are getting closer to the finish line. There are just two steps left. We are focusing on one of them now: Adding a route. For that, we need to ask ourselves what route we want to add. In this article, I want to add two different GET routes one that gets all posts and one that gets an article by its ID. Let's get started by implementing our route in the file *post.js*.
```JavaScript
const express = require('express');

const router = express.Router();

// Controller Methods
const { getPosts, getPostById } = require('../controllers/postsController');

router.route('/')
  .get(getPosts);

router.route('/:id')
  .get(getPostById);

module.exports = router;
```
The *express router* lets us define routes based on HTTP verbs like GET, POST, etc. We just need to add our controller methods, that we will implement later, to the HTTP verb, and the *router* will do his magic. 
In *server.js* we need to add the router like this:
```JavaScript
// Between helmet and custom middleware:
// All routes here
app.use('/api/posts', require('./routes/post'));
```
This will throw an error because we did not implement the *controller functions* yet.
#### Controllers
Now we are at the last step for our REST-API template. The controller functions. We will need to create two of them, *getPosts* and *getPostById*. Let's get to work by implementing these methods in *postsController.js*.
```JavaScript
const postsArray = [
  {
    id: 1,
    title: 'React from scratch',
    content: 'In this article we will create a ToDo App in React from scratch.... etc.etc.etc.',
    author: 'Jakob Klamser'
  },
  {
    id: 2,
    title: 'Vanilla JavaScript Basics',
    content: 'Today we will discuss some basic JavaScript fundamentals like array manipulation, currying etc.',
    author: 'Jakob Klamser'
  },
  {
    id: 3,
    title: 'VS Code Extensions',
    content: 'I wanted to show you some of my favorite VS Code Extensions.... Bracket Pair Colorizer etc.etc.',
    author: 'Jakob Klamser'
  },
  {
    id: 4,
    title: 'ExpressJS REST API',
    content: 'Is this the current article?',
    author: 'Jakob Klamser'
  },
];


// @route   GET api/posts
// @desc    Get All Posts
// @access  Public
exports.getPosts = (req, res) => {
  const posts = postsArray;
  return res.status(200).json({
    success: true,
    count: posts.length,
    data: posts,
  });
};

// @route   GET api/posts/:id
// @desc    Gets a post by ID
// @access  Private
exports.getPostById = (req, res) => {
  const post = postsArray.filter(post => post.id === Number(req.params.id));
  console.log(post);
  if (post[0] !== undefined) {
    return res.status(200).json({
      success: true,
      data: post[0],
    });
  }
  return res.status(404).json({
    success: false,
    error: 'No post found',
  })
};
```
At the top of the file, we have some static data. After that, we export two functions. The first one, getPosts, returns the whole list of static data. The second method, getPostById, returns one object from the array if the id matches or it returns an error if no post matches the id provided in the request.

The last thing we need to do is enable JSON for our application by adding another middleware.
```JavaScript
// Right below helmet:
app.use(express.json());
```

### Conclusion
You can now type in http://localhost:5000/api/posts or http://localhost:5000/api/posts/2 to access the API (while it is running).
I hope you enjoyed this quick guide for setting up a template express API.
You can build on that by adding a database, authentication and authorization, more endpoints, and so on.
Let me know what you think about it and if you build something on top of this template.
The whole project can be found on my [GitHub](https://github.com/JakobKIT/express_api_template).


<span>Photo by <a href="https://unsplash.com/@sushioutlaw?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Brian McGowan</a> on <a href="https://unsplash.com/s/photos/express?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></span>