Hands-On Guide: Building Efficient APIs with Node.js - Part 1

Hands-On Guide: Building Efficient APIs with Node.js - Part 1

To know more about the technologies used in API development. First part of a three part series.

If you are a backend engineer, REST API is one of the most important topics you’ll have to master. Before we start building our project, let us understand a few things about REST API.

Getting Started with REST API | Dell Technologies Info Hub

REST API

  1. REST APIs have certain endpoints to which front-end applications or other systems could make a request to perform certain tasks.

  2. REST APIs are stateless i.e. they don’t retain information (to be more technical, they don’t create sessions between frontend devices and the server)

  3. Since REST APIs are stateless, anyone on the web can make calls to our API. So, we need to come up with an authentication method. We will cover this in the second part of this series.

  4. JSON and XML are usually used to transfer data between the API and devices that are making calls to the API.

Prerequisites

Maku sure the following are installed on your device:

  1. Node.js

  2. Postman (if not installed, Web version can also be used).

  3. Git

Without further ado, let’s start building our API.

API Endpoints

We are going to develop a simple text sharing platform where users could broadcast a text message to all users on the platform.

We will need the following routes

  1. Users

    1. get all the users (get request).

    2. get a user by ID (get request).

    3. login a user (post request).

    4. signup (post request).

    5. edit the details of a user (patch request).

    6. delete a user (delete request).

  2. Messages

    1. get all the messages (get request).

    2. get (a message) by ID (get request).

    3. get messages by user ID (get request).

    4. post a new message (post request).

    5. edit a message (patch request).

    6. delete a message (delete request).

Project Structure

  1. app.js contains the main code.

  2. api/models contains the database schema used in the project.

  3. api/routes contains the API endpoints.

  4. api/controllers contains the API endpoint functions (generally done to improve MVC).

  5. api/middleware contains the functions that are executed before passing on the request to our endpoints.

Installing Dependencies

npm install express morgan body-parser mongoose nodemon
  1. express framework is used to develop the entire API.

  2. morgan is used for better logging.

  3. body-parser is used to parse the JSON file sent to the backend.

  4. mongoose is a popular ODM for MongoDB and Node.js. It emphasizes more on predefined schema types which provides a SQL-ish behavior in a NoSQL database such as MongoDB.

  5. nodemon is used to restart the server upon changes, which is truly time saving for many developers out there.

  6. Add the above following in the package.json file.

     "scripts": {
         "start" : "nodemon app.js"
     }
    

Code

Let’s start building.

app.js

// Import required modules
const express = require('express'); 
const bodyParser = require('body-parser'); 
const morgan = require('morgan'); 
const mongoose = require('mongoose'); 
const app = express(); // Initialize the Express app
const port = process.env.port || 3000; // Define the port for the server

// Use Morgan middleware to log HTTP requests in the console
app.use(morgan('dev'));

// Use bodyParser middleware to parse incoming JSON data
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());

// Define a route for the root URL ("/") to respond with a JSON message
app.get("/", (req, res) => {
    res.status(200).json({
        message: "Express server says hello!"
    });
});

// Start the server to listen on the specified port
app.listen(port, () => {
    console.log(`Server is running on port ${port}`);
});

// The above code initializes the Express app, sets up middleware,
// defines a simple route, and starts the server on the specified port.

We have imported all the necessary modules, and our API should now return a response. We are returning a message “Express server says hello!” upon a get request to our server. For more information about Express middleware and how they work, refer to the official documentation. Now let’s run our server. Open the terminal in the current directory and run the following script.

npm start

Open localhost:3000 and verify (http://localhost:3000)

Error Handling Middleware

// Middleware to handle requests for endpoints that don't exist (404 Not Found)
app.use((req, res, next) => {
    const error = new Error('Not Found'); 
    // Create a new Error instance with a message
    error.status = 404; 
    // Set the HTTP status code to 404 (Not Found)
    next(error); 
    // Pass the error to the next middleware in the stack
});

// Generic error handling middleware for both user and unexpected errors
app.use((error, req, res, next) => {
    res.status(error.status || 500); 
    // Set the response status code based on the error status or default to 500 
    res.json({
        error: {
            message: error.message 
            // Send a JSON response with an error message
        }
    });
});

The first middleware executes when a request is made to an API endpoint that doesn’t exist. The second middleware handles both types of errors, including explicit errors thrown by users and unexpected errors. These two error handlers are generally recommended by Express framework. Let’s define our API endpoints now.

api/routes/Users.js

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

router.get('/', (req, res) => {}); // Get all users
router.get('/:id', (req, res) => {}); // Get a user by ID
router.post('/', (req, res) => {}); // Create a new user
router.patch('/:id', (req, res) => {}); // Update a user by ID
router.delete('/:id', (req, res) => {}); // Delete a user by ID

module.exports = router;

api/routes/Messages.js

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

router.get('/', (req, res) => {}); // Get all users
router.get('/:id', (req, res) => {}); // Get a user by ID
router.get('/users/:id', (req, res) => {}); // Get messages by user ID
router.post('/', (req, res) => {}); // Create a new message
router.patch('/:id', (req, res) => {}); // Update a message by ID
router.delete('/:id', (req, res) => {}); // Delete a message by ID

module.exports = router;

Now, import the above codes into our app.js file.

const userRoutes = require('./api/routes/Users');
const messageRoutes = require('./api/routes/Messages');
app.use('/users', userRoutes);
app.use('/messages', messageRoutes);

Our final app.js code

const express = require('express');
const bodyParser = require('body-parser');
const morgan = require('morgan');
const mongoose = require('mongoose');
const app = express();
const port = process.env.port || 3000;
const userRoutes = require('./api/routes/Users');
const messageRoutes = require('./api/routes/Messages');

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

app.use('/users', userRoutes);
app.use('/messages', messageRoutes);
app.get("/", (req, res) => {
    res.status(200).json({
        message : "Express server says hello!"
    });
});
app.use((req,res,next)=>{
    const error = new Error('Not Found');
    error.status = 404;
    next(error);
});
app.use((error, req,res,next)=>{
    res.status(error.status || 500);
    res.json({
        error : {
            message : error.message
        }
    });
});

app.listen(port, () => {
    console.log(`Server is running on port ${port}`);
});

We’ve successfully created the structure to serve our API. Repository:github.com/SanjayNithin2002/substack-api

Next Stop: Database Integration: In our next post, we'll connect our API to a database, paving the path for dynamic data management: Hands-On Guide: Building Efficient APIs with Node.js - Part 2