You are currently viewing Building a RESTful API with Express.js: Adding GET, POST, PUT, and DELETE Endpoints

Building a RESTful API with Express.js: Adding GET, POST, PUT, and DELETE Endpoints

In one of my previous posts, I setup an express server in node.js. That post can be found here.  

In this hands-on, I will be modifying the same code and will have a proper code structure (which I usually use) and will expose the following end point in it.

  1. Get
  2. Post
  3. Put
  4. Delete
So without any delay, let us start and fire up your code editor. In my case, It is Visual Code.

Setting Up Code Structure

In order to set up the code structure,  navigate to the root folder of your project and create the following folders:-

  • src
    • routes
    • models
    • controllers

I have created a folder named “src” at the root of the project. My all code files and folders will be inside this src folder. The javascript file which resided on the root folder with the name “app.js” (which is the main entry point of my project) will also be moved into the src folder. Once you complete these steps, the project structure will be something like below:-

 

Project Structure

Including Modules In your project (Common.js and ES Modules)

In the current code, you can see that we have added express and dotenv to our project. In order to do that, I have used the keyword “require” in my code which is part of the CommonJS module system. CommonJS is a module system that is widely used in older versions of Node. It is still being used widely in Node.

On the other side, ES Modules are the standard module system that was introduced in ECMAScript 6 (ES6) and later versions. 

The use of CommonJS or ES Modules totally depends on your project structure. However, it is recommended to use ES Module if you have a newer version of the Node. If you have a Node version that is prior to 14, then use the CommonJS.

I am using a node version 18.16.0, so  I will modify my code to use the import syntax (ES Modules), instead of using CommonJS.

To do that, I will change the first two lines of my code as below:-

 

import express from 'express'
import dotenv from "dotenv"

Once I made these changes, I started to face the following error when I compile my code.

PS F:\Hands on Tutorial\Node.js\CreateExpressServer\src> node app.js   
(node:3592) Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension.
(Use `node --trace-warnings ...` to show where the warning was created)
F:\Hands on Tutorial\Node.js\CreateExpressServer\src\app.js:1
import express from 'express'
^^^^^^

SyntaxError: Cannot use import statement outside a module
    at internalCompileFunction (node:internal/vm:73:18)
    at wrapSafe (node:internal/modules/cjs/loader:1176:20)
    at Module._compile (node:internal/modules/cjs/loader:1218:27)
    at Module._extensions..js (node:internal/modules/cjs/loader:1308:10)
    at Module.load (node:internal/modules/cjs/loader:1117:32)
    at Module._load (node:internal/modules/cjs/loader:958:12)
    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)
    at node:internal/main/run_main_module:23:47

In order to use the ES Module in our node application, we need to configure it so that it can recognize that we intend to use the ES Module. In order to do that, we need to address the warning which is being shown in Line # 2. So in order to do that, open up the package.json and add “type”: “module” in it as shown in below diagram.

Setting ES Module in Package

Once the above change is done, our project will run without any error:-

PS F:\Hands on Tutorial\Node.js\CreateExpressServer\src> node app.js   
Port:8089
Server is listening on Port: 8089

Setting Up Nodemon

Our Node application is running as can be seen in the last output. However, it is a very repetitive task to run the node application whenever I make some changes. In order to get rid of it, we need to have some mechanism that restarts our node application as soon as I make some changes. In order to install nodemon, we need to run the following command in our terminal:-

npm install --save-dev nodemon

In the above code, I have used the normal “install” command of npm with a difference of flag “–save-dev“. This flag will make sure that this “nodemon” dependency is only required for development and not for production. 

The reason is to add nodemon as dev dependency is obvious i.e. we need to run out node application as soon as we make some changes in the code. This is a thing, which we do not need in production.

Once the demon is added as a dev dependency, it will be reflected in our package.json as shown below:-

{
  "name": "createexpressserver",
  "version": "1.0.0",
  "description": "",
  "type": "module",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "dotenv": "^16.0.3",
    "express": "^4.18.2"
  },
  "devDependencies": {
    "nodemon": "^3.0.1"
  }
}

In the above code, you can see that nodemon’s latest version 3.0.1 is installed as the dev dependency in our project at line # 17 to 19.

Now as the last step, I also need to write a start command in our scripts section of the package.json. The command which I am going to add is given below:-

"start": "nodemon ./src/app.js",

Once the above script is added to our scripts section in the package.json, we can use the command “npm start” to run our application. Once the application is running, change something in the code and the application will be restarted as shown below:-

PS F:\Hands on Tutorial\Node.js\CreateExpressServer\src> npm start
Debugger attached.

> createexpressserver@1.0.0 start
> nodemon ./src/app.js

Debugger attached.
[nodemon] 3.0.1
[nodemon] to restart at any time, enter `rs`  
[nodemon] watching path(s): *.*
[nodemon] watching extensions: js,mjs,cjs,json
[nodemon] starting `node ./src/app.js`        
Debugger attached.
Port:8089
Server is listening on Port: 8089
[nodemon] restarting due to changes...
[nodemon] starting `node ./src/app.js`
Debugger attached.
Port:8089
Server is listening on Port: 8089

In the above output, it can be seen that I have started my application by using the “npm start” command which will execute the nodemon command as mentioned in the package.json. In addition to that, it can be seen that our application restarted as soon as I made a change in the code. It can be seen in line # 16.

Defining route and mounting it

Before defining and mounting the route, we need to understand what is the route. The route basically determines how an application responds to a client request. Client request includes a path, method, and type of method. Based on this information, routes determine to which endpoint request should be forwarded.

In our existing code, we have defined a router in our app.js file. It is not an error or bug but in order to organize our code structure, I will be adding all routes of my applications under the routes folder. I will be adding all routes which are related to my application under the routes folder. It is possible that I will have more than one collection or business object, so I will be writing a route for each object in its relevant file. So, let us add a route for Person. This route will be responsible to get data from the Person controller and will return to the client. 

Add a file named “person.router.js” in your routers folder, and add the following code in this newly created file.

import express from 'express';
const Personalrouter = express.Router();

// Define Get For Persons. This router will co-ordinate with the controller 
// to get all persons
Personalrouter.get('/', (req, res) => {
    // Logic to fetch users from a database
  const persons = [
    { id: 1, name: 'John Doe' },
    { id: 2, name: 'Jane Smith' },
  ];
  res.json(persons);
});

// Define Post For Persons. This router will co-ordinate with the controller 
// to create a new Person
Personalrouter.post('/persons', (req, res) => {
  // Logic to create a new user
  const newPerson = req.body;
  
  // Assuming validation and database saving steps
  
  res.status(201).json(newPerson);
});

// Define Put For Persons. This router will co-ordinate with the controller 
// to create a new Person
Personalrouter.put('/persons', (req, res) => {
    // Logic to create a new user
    const modifyPerson = req.body;
    
    // Assuming validation and database saving steps
    
    res.status(200).json(modifyPerson);
  });
  //Define the delete endpoint to delete a person
  Personalrouter.delete('/persons', (req, res) => {
    // Logic to create a new user
    const deletedPerson = req.body;
    
    // Assuming validation and database saving steps
    
    res.status(200).json("Person deleted successfully!");
  });
export default Personalrouter;

In the above code, I have defined the following:-

  1. A get method which at the moment is returning dummy persons data.
  2. Defined a post method, which accepts the data and creates a new person. At this point in time, it is returning the accepted data and returning it as is with a status of 201, which means a new record is created.
  3. Then I defined a put method, which is used to update an existing person. It is also returning dummy data with 200 status.
  4. In last, I defined a delete method that will be used to delete a person’s record.
  5. At the very last line, I have exported this Personrouter, so that it can be imported in other files. If this code is missed, we will not be able to import it into other files.

Mounting the custom router i.e. "personRouter" in the Express App

As our route is completed for persons, it is now time to mount this route in our express application. In order to do that first, we need to import it in our app.js. Then we need to mount it using the app. use. Both these steps are given below:-

// Import the person router
import Personalrouter from './routers/person.routers.js';
const app = express();
app.use(demoMiddleware)
// Mount the custom route
app.use('/person', Personalrouter);

In the above code, we are achieving the following:-

  1. At line # 2, importing the Personalrouter
  2. At line # 6, we are mounting this router against the path “/person”

 

Output

At this point in time, if you run the application and navigate to the “localhost:8089/person/” you will get all the persons which we are returning from the router as shown below:-

Create Controller

Before adding a controller, let’s discuss the purpose of the controller.

In Node.js and even in .Net, the purpose of the controller is to handle incoming requests, managing the application logic, and co-ordination with models.

e.g. You have an application, where there is a button to “Get all the persons”. Once user clicks on this button, the express server will receive the request. Routes will determine to which controller and to which controller’s method the request should be routed. Once the controller receives the request, it will get the data from the database, it will fill the data in the desired models and will send back the result. 

 

Create the static data

In our router, I have returned the hard-coded data which represents a person. In the real world, the router is responsible to do only routing. So while creating the controller, I will be returning the data from the controller, and its relevant code from the router will be deleted. First, create a js file named “person.controller.js and then add hard-coded data in it as shown below:-

const persons = [
    { id: 1, name: 'John Doe' },
    { id: 2, name: 'Jane Smith' },
  ];

Add Methods to handle the Get Request for Persons

In our newly created controller, I will add two methods that will be responsible to handle the “Get Request” for a person. 

Get All Persons

Get All persons will be a method, which will be responsible to get all the persons from the database. In our case, it will return all the data inside the person’s variable. The following code is self-explanatory. It accepts “request and response” as input parameters and then set all the persons (i.e. persons) as response.

 // Method to fetch all all persons
  const getAllpersons = (req, res) => {
    res.json(persons);
  };

Once the above method is ready, it’s time to export it, and then we will utilize it in our person.router.js file. Add the following line at the end of the person.controller.js. If we do not add the below line, we will not be able to use this method in our router.

export { getAllpersons };

Complete Code for Controller (Including corresponding methods for Post, Put and Delete)

In the same way, we wrote the method for Get All Person, I will add all methods to “get person by id” , create, update and delete the person. The entire code is given below

Modify the router code to call the controller

Let us modify the endpoint which is responsible to get all the persons. In our “person.router.js“, the very first “get” is responsible to get all the persons. So, I will modify it to invoke the getAllPersons from the person.controller.js. Its modified code is given below:-

import { getAllpersons,getPersonById,createPerson } from '../controllers/person.controller.js';
// Define Get For Persons. This router will co-ordinate with the controller 
// to get all persons
Personalrouter.get('/',getAllpersons);

In the above code, I have imported all the methods which I defined in the controller and modified the “get” to call the getAllPersons. Now lets run the application and open up the browser and navigate to the following URL:-

https://localhost:8089/person

If everything is done correctly, you will see the following output. I have used Insomnia instead of the browser. Insomnia is identical to Postman. Both these tools are used to test APIs.

Get All Persons
let persons = [
    { id: 1, name: 'John Doe' },
    { id: 2, name: 'Jane Smith' },
  ];
  
  // Method to fetch all all persons
  const getAllpersons = (req, res) => {
    res.json(persons);
  };
  // Method to fetch a single person by ID
  const getPersonById = (req, res) => {
    const { id } = req.params;
    const person = persons.find((person) => person.id === parseInt(id));
  
    if (person) {
      res.json(person);
    } else {
      res.status(404).json({ error: 'person not found' });
    }
  };
  
  // Method to create a new person
  const createPerson = (req, res) => {
    const newPerson = req.body;
    
    persons.push(newPerson);
  
    res.status(201).json(newPerson);
  };
  //Method to update an existing person
  const updatePerson = (req, res) => {
    const personId = parseInt(req.params.id);
    const updatedPerson = req.body;
    // Find the index of the person with the given ID in the array
    const index = persons.findIndex((person) => person.id === personId);
    if (index === -1) {
      return res.status(404).json({ error: 'Person not found' });
    }
    // Update the person in the array
    persons[index] = { ...persons[index], ...updatedPerson };
    res.json({ message: 'Person updated successfully', updatedPerson: persons[index] });
  };
  // Method to delete a person
  const deletePerson = (req,res) =>{
    const personId = parseInt(req.params.id);
    const index = persons.findIndex((person) => person.id === personId);
  
    if (index !== -1) {
      const deletedPerson = persons.splice(index, 1)[0];
      res.json({ message: 'Person deleted successfully', deletedPerson });
    } else {
      res.status(404).json({ error: 'Person not found' });
    }
  }
  // Export the methods
  export { getAllpersons, getPersonById, createPerson, updatePerson, deletePerson };
  

Modified code for the "person.router"

AS in the previous step, I have written all the code of Getting Person, Update Person, Create Person, and Delete Person in the person.controller.js. So now I need to rewrite my code of the router, so that router only redirects requests to the controller as the rest of the things are being done at the controller level. The complete modified code is given below:-

import express from 'express';
import { getAllpersons,getPersonById,createPerson, updatePerson, deletePerson } from '../controllers/person.controller.js';
const Personalrouter = express.Router();

// Define Get For Persons. This router will co-ordinate with the controller 
// to get all persons
Personalrouter.get('/',getAllpersons);
// This is also a get method. It accepts the  
Personalrouter.get('/:id', getPersonById)
// Define Post For Persons. This router will co-ordinate with the controller to create a new person
Personalrouter.post('/', createPerson);
// Define Put For Persons. This router will co-ordinate with the controller to update an existing person
Personalrouter.put('/:id', updatePerson);
//Define the delete endpoint to delete a person
Personalrouter.delete('/:id',deletePerson);

export default Personalrouter;

In the above code, I have imported all the methods from the person.controller.js into my router file i.e. person.router.js. Then in the code, I have called corresponding methods from the controller into the router against each route. Since we have already mounted our PersonalRouter in our express application by using the “app.use(“/person”/, PersonRouter)”, so now is the time to check all the methods.

Final Result

Get All Persons

Get All Persons

Get Person By Id

Get Person By ID

Create New Person

Update an Exising Person

Update an Existing Person

Delete a Person

Delete Person
const Personalrouter = express.Router();
// Middleware to parse JSON data in the request body
Personalrouter.use(express.json());

Make sure to write this line just beneath the code line where I’ve declared the PersonalRouter.

Summary:-

In this post, the following have been discussed:-

  • Setting up code structure including
    • Models (Not used in this post)
    • Controller
    • Router
  • Installing and using Nodemon
  • How to mount the custom router
  • How to use the controller’s method in the router
  • Wrote Endpoints for 
    • Get Request
    • Post Request
    • Put Request
    • Delete Request

 

Source Code

You can find the source code of this post on the following GitHub URL:-

https://github.com/shahidriaz/https—github.com-shahidriaz-nodeexpressserver

Leave a Reply