Friday, March 29, 2024

RESTful Web Services: Modifying Data

This series on RESTful Web services covers how to implement both a RESTful Web Service and client component using Node.js, Express.js, and JavaScript. In the Creating Your First Web Service tutorial, we learned how to set up a basic Web service using Node.js and the Express framework. In Part 2, we adapted our web server to handle different request types. In today’s instalment, we’ll build upon what we’ve accomplished thus far by implementing methods to modify data via the POST, PUT, and DELETE requests.

Required Middleware

Express is an application framework that provides a robust set of features specifically for web and mobile applications. It maintains its svelte and light-weight profile by including a minimal feature set out of the box. To provide additional functionality, it relies on middleware extensions – so named because they operate in the “middle” of a request. There are a few built-in extensions (such as express.static, which we are using), but most are provided by third parties. One such extension is “body-parser”; it helps decode the body from HTTP POST requests. Here’s the code to include the body-parser extension:

const bodyParser = require("body-parser");
// ...
// Configuring body parser middleware
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());

Since our server is performing double duty as both our Web server and API endpoint, there are no cross-origin concerns. However, in most cases, you’ll need to include middleware for that:

const cors = require('cors');
// ...
app.use(cors());

Adding a New Object Using the Body-parser Extension

The time has come to put the body-parser extension to use inside the app.post() handler to retrieve the request body. But first, let’s convert the vehicles object into an array so that it’s easier to modify:

let vehicles = [
  {
    id: 1,
    make: "Honda",
    model: "Accord",
    year: 2020,
    color: "grey",
    plateNumber: "ES6 3G6",
    vin: "1G1ZT51806F128009"
  },
  {
    id: 2,
    make: "Infiniti",
    model: "G37",
    year: 2009,
    color: "blue",
    plateNumber: "RW4 993",
    vin: "JH4KA3240HC002301"
  },
  {
    id: 3,
    make: "Porsche",
    model: "Macan",
    year: 2019,
    color: "red",
    plateNumber: "6HG 2ER",
    vin: "JH4KA3160KC018606"
  }
];

Now all we have to do to add a new vehicle is push it onto the vehicles array!

app.post("/vehicles", (req, res) => {
  const vehicle = req.body;
  
  vehicles.push(vehicle);
  
  // output the vehicle to the console to show that we got it
  console.log(vehicle);
  
  res.send("Vehicle has been added to inventory");
});

Modifying an Object

As stated earlier, objects may be modified via either a POST or PUT HTTP call. As to which one is best to use, it’s really a matter of preference and practicality. If you expect clients to consume your service using a browser, then POST is your best bet. Otherwise, it doesn’t make much difference. Having said that, you’ll face the same issue for delete operations, which rely on the HTTP DELETE request. That being the case, we might as well give both POST and PUT a try.

Using POST/PUT

Just as a GET request URL that ends with an ID adds a new object, a POST (or PUT) request that includes a vehicleId may be employed to modify (or replace) the vehicle specified by the ID. Therefore, we have to retrieve the vehicleId from the request params array. How you locate the object to modify will depend on your datasource, but in our case, we have to do a lookup by vehicleId. I am using the Array findIndex() method to retrieve the index of the vehicle to update. It is usually a good idea to include error handling for IDs that don’t match up with any existing ones. Right now, it’s simple enough to send a vehicleId that can’t fail, but once a delete endpoint has been implemented, there is no longer any guarantee that a given vehicle will exist. Here’s the full code for the POST update handler:

app.post("/vehicles/:vehicleId", (req, res) => {
  // Read the vehicleId from the URL
  const vehicleId = req.params.vehicleId;
  const newVehicle = req.body;
  const vehicleIndex = vehicles.findIndex((vehicle) => vehicle.id == vehicleId);

  if (vehicleIndex === -1) {
    return res.status(404).send(`No vehicle with ID ${req.params.vehicleId}!`);
  } else {
    vehicles[vehicleIndex] = newVehicle;
    
    // output the vehicle to the console for debugging
    console.log(vehicles);
    
    return res.send(`Vehicle ${vehicleId} has been updated.`);
  }
});

To handle PUT requests, your server would need to implement a PUT handler like the following:

app.put("/vehicles/:vehicleId", (req, res) => {
  // same code as the POST handler...
});

Deleting an Object

When it comes to deleting entities, we typically only allow the deletion on a one-by-one basic to avoid an accidental catastrophic data loss. To delete items, we use the HTTP DELETE method and specify a vehicle by its vehicleId, as we’ve done previously:

app.delete("/vehicles/:vehicleId", (req, res) => {
  // Read the vehicleId from the URL
  const vehicleId = req.params.vehicleId;
  const vehicleIndex = vehicles.findIndex((vehicle) => vehicle.id == vehicleId);

  if (vehicleIndex === -1) {
    return res.status(404).send(`No vehicle with ID ${vehicleId}!`);
  } else {
    vehicles.splice(vehicleIndex, 1);

    // output the vehicle to the console for debugging
    console.log(vehicles);

    return res.send(`Vehicle ${vehicleId} has been deleted.`);
  }
});

We can view the output of subsequent delete actions in our demo below:

js web services

On the first delete, all is well, but attempting to delete the same object a second time results in a 404 error.

Conclusion

This concludes the server-side equation of this series on RESTful Web Services. Coming up next: how to consume RESTful APIs using a Web browser. There’s a good example right here in the index.html file of today’s demo.

Rob Gravelle
Rob Gravelle
Rob Gravelle resides in Ottawa, Canada, and has been an IT guru for over 20 years. In that time, Rob has built systems for intelligence-related organizations such as Canada Border Services and various commercial businesses. In his spare time, Rob has become an accomplished music artist with several CDs and digital releases to his credit.

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Popular Articles

Featured