DEV Community

Cover image for Building APIs Using Express.JS
bewarusman
bewarusman

Posted on

14 5

Building APIs Using Express.JS

Summary

In this post, I will show you how to build a blog web API in Node.JS. This tutorial uses Express.JS for handling HTTP requests and Mongodb for storing data.

Table Of Contents

Introduction

Node.JS is a platform used for building server side applications using Javascript. With Node.JS, developers are able to build backend APIs in minutes. It has a great community and a huge set of packages. These packages help the developers for building great applications. Developers does not require to build everything from scratch. We mainly focus on two packages. First is Express.JS, which is one of the most used packages by developers to build web APIs. Second is mongoose, which is used to simplify the communication between Node.JS and MongoDB.

Requirements

  • Basic Javascript Knowledge
  • Node.JS 10.0.0 or higher
  • NPM 4.6.1 or higher
  • Mongodb 4.2.1 or higher
  • VS-Code or any other editor

Setup

A typical Node.JS application has a root directory which contains at least two files package.json (holds metadata about the application and required npm packages), and index.js file (a javascript entry file).

  • Create the directory of the project
mkdir blog-server
cd blog-server
Enter fullscreen mode Exit fullscreen mode
  • Create package.json file
npm init -y
Enter fullscreen mode Exit fullscreen mode
  • Create index.js file (entry file)
// index.js
const PORT = 3000;
console.log(`A node.js server that runs on ${PORT}`);
Enter fullscreen mode Exit fullscreen mode
  • Run the application
node index.js
Enter fullscreen mode Exit fullscreen mode

Packages

Our express.js web application requires these packages.

  • express: routing and middleware web framework
  • cors: enables CORS (cross-origin resource sharing)
  • body-parser: parses json body into javascript object
  • morgan: log http requests, important for seeing the request
  • mongoose: mongodb ORM
  • nodemon: eases development by restarting the server on any change

NOTE: nodemon is used as a dev-dependency because it is required only during the development time.

  • Install packages from NPM.
npm install --save-dev nodemon
npm install --save express cors body-parser morgan mongoose
Enter fullscreen mode Exit fullscreen mode
  • Import packages using require inside index.js file.
const express = require("express");
const cors = require("cors");
const bodyParser = require("body-parser");
const morgan = require("morgan");
const mongoose = require("mongoose");
Enter fullscreen mode Exit fullscreen mode

Database

As mentioned above, we are using Mongodb for storing application related information. We use mongoose as object mapper between Mongodb and node.js application models.

  • Connect to mongodb
mongoose.connect("mongodb://localhost:27017/blog");
Enter fullscreen mode Exit fullscreen mode
  • Create mongoose schema to define the structure of the document that is read from or write to Mongodb. Create a schema named postSchema to define the structure of posts, which it has title and body.
const postSchema = new mongoose.Schema(
   {
      title: { type: String, required: true },
      body: { type: String, required: true },
   },
   { timestamps: true }
);
Enter fullscreen mode Exit fullscreen mode

MVC Like Application

An MVC app is structured into three layers [models, views, controllers]. Sometimes, extra layers are added to MVC such as DAL, Services, Repositories.
In this example, the app is divided into three layers [models → services →controllers]. Usually, each layer exists in a directory.

Models

Models represent domain specific data. The model is based on postSchema defined above.

  • create a Post model.
const Post = mongoose.model("post", postSchema);
Enter fullscreen mode Exit fullscreen mode

Services

Service layer is an additional layer in MVC that mediates communication between a Controller and a Model. This layer adds more abstractions and ease of testability.
Create a postService entity which exposes two services:

  1. find: to query all post data
  2. save: to save a post
const postService = {

   find: () => Post.find({}),

   save: async (postData) => {
      const post = new Post({ ...postData });
      await post.save();
      return post;
   },
};
Enter fullscreen mode Exit fullscreen mode

Controllers

Controllers as the name implies, controls the incoming request, catches errors and sends back a response to the client.
Create a postController which it has two actions:

  1. find: handles GET /api/posts
  2. save: handles POST /api/posts
const postController = {

  find: async (req, res, next) => {
      try {
         const posts = await postService.find({ ...req.query });
         res.json(posts);
      } catch (error) {
         error.msg = "failed to retrieve posts";
         next(error);
      }
   },

   save: async (req, res, next) => {
      try {
         const post = await postService.save(req.body);
         res.json(post);
      } catch (error) {
         error.msg = "failed to create post";
         next(error);
      }
   },
};
Enter fullscreen mode Exit fullscreen mode

Express Application

Express is a routing and middleware web framework that has minimal functionality of its own: An Express application is essentially a series of middleware function calls.

  • Create an express application
const app = express();
Middlewares
Middlewares are functions executed before or after the controller actions.
app.use(cors());
app.use(morgan("tiny"));
app.use(bodyParser.json());
Enter fullscreen mode Exit fullscreen mode

Express Router

The express router route the request to a specific action in the controller.
Define two routes based on Express Router to handle

  1. GET /api/posts
  2. POST /api/posts
const router = express.Router();
router.get("/posts", postController.find);
router.post("/posts", postController.save);
app.use("/api", router);
Enter fullscreen mode Exit fullscreen mode

Complete Example

I have included a complete express server example.

/** STEP 1: Packages */
// For a minimal node.js server we need to install and import the bellow packages
const express = require("express"); // fast, open-source node.js server
const cors = require("cors"); // enables CORS (cross-origin resource sharing)
const bodyParser = require("body-parser"); // parses json body into javascript object
const morgan = require("morgan"); // log http requests
const mongoose = require("mongoose"); // mongodb orm
/** STEP 2: DATABASE */
// connect to mongodb
mongoose.connect("mongodb://localhost:27017/blog", {
useNewUrlParser: true,
useUnifiedTopology: true,
useCreateIndex: true,
useFindAndModify: false,
});
// create mongoose schema
const postSchema = new mongoose.Schema(
{
title: { type: String, required: true },
body: { type: String, required: true },
},
{ timestamps: true }
);
/** STEP 3: MVC Like App */
// An MVC app is structured into [Models, Views, Controllers]
// A typical API does not to expose views.
// Sometimes, extra layers are added to MVC such as DAL, Services, Repositories
// In this example, the app is divided into 3 layers [Models -> Services -> Controllers]
// Note: usually, each layer exists in a directory.
// MODELS: represents domain-specific data
// post model - built based on mongoose schema postSchema [defined above]
const Post = mongoose.model("post", postSchema);
// SERVICES: is an additional layer in MVC that mediates communication between a Controller and a Model
// Note: this layer adds more abstractions and ease of testability
// post service
const postService = {
// find service - uses post model to query all posts
find: () => Post.find({}),
// save service - uses post model to save a post
save: async (postData) => {
const post = new Post({ ...postData });
await post.save();
return post;
},
};
// post controller - each action in the controller, handles a specific route.
const postController = {
// GET /api/posts
find: async (req, res, next) => {
try {
const posts = await postService.find({ ...req.query });
res.json(posts);
} catch (error) {
error.msg = "failed to retrieve posts";
next(error);
}
},
// POST /api/posts
save: async (req, res, next) => {
try {
const post = await postService.save(req.body);
res.json(post);
} catch (error) {
error.msg = "failed to create post";
next(error);
}
},
};
// routes - define the routes based on express Router
const router = express.Router();
router.get("/posts", postController.find); // GET /api/posts
router.post("/posts", postController.save); // POST /api/posts
/** STEP 4: Express Server */
// create an express app
const app = express();
// use third-party middlewares
app.use(cors());
app.use(morgan("tiny"));
app.use(bodyParser.json());
// handle routes
// use the router within the express app to handle routes defined above
app.use("/api", router);
// use custom middlewares if any
// handle a request if url path is not found
const notFound = (req, res, next) => {
res.status(404);
res.json({
status: 404,
error: "not found",
});
};
// handle an error if occured in the controller
const handleError = (error, req, res, next) => {
console.log(error);
res.status(error.status || 500);
res.json({
message: error.message || "failed: not known error",
msg: error.msg,
stack: error.stack,
});
};
app.use(notFound); // in-case a url path is not found
app.use(handleError); // in-case an error has occured
// Run the server
// running the server on a port to listen to HTTP requests.
const PORT = 3000;
app.listen(PORT, (err) => {
if (err) console.log(err);
else console.log(`Server started on port: ${PORT}`);
});
view raw one-file.js hosted with ❤ by GitHub

Conclusion

You have learnt in detail, how to create an express server and connect to mongodb for storing data. You have exposed some APIs. In this tutorial I have written all code in one file for simplicity. You can visit this repository for the complete example.

Image of Docusign

🛠️ Bring your solution into Docusign. Reach over 1.6M customers.

Docusign is now extensible. Overcome challenges with disconnected products and inaccessible data by bringing your solutions into Docusign and publishing to 1.6M customers in the App Center.

Learn more

Top comments (2)

Collapse
 
wparad profile image
Warren Parad

This is great, but you likely need to also insert security in everyone of these endpoints. I made some suggestions in this post: dev.to/wparad/validating-jwts-in-w...

Collapse
 
bewarusman profile image
bewarusman

Thanks for your comment. Sure, I will add JWT authentication as soon as possible.

Image of Docusign

🛠️ Bring your solution into Docusign. Reach over 1.6M customers.

Docusign is now extensible. Overcome challenges with disconnected products and inaccessible data by bringing your solutions into Docusign and publishing to 1.6M customers in the App Center.

Learn more