Exercise - Read and write

Completed

The online retailer is impressed with your first web application. Now it wants you to create an API that you can read and write from. The data might be stored in a database and might contain millions of records. For that reason, the retailer wants to see an app that uses techniques that limit how much data is asked for.

Implement support to read and write data

It's common to construct an API with a number of resources. Each resource can have several operations available on it to read and write. Organizing by resource and by operations like read/write is called create, read, update, delete (CRUD). Implement the CRUD API on the products resource:

  1. Clone the node-essentials repo by running the following command.

    Note

    If you completed this step in the earlier exercise, you don't need to do it again.

    git clone https://github.com/MicrosoftDocs/node-essentials
    

    This starter project contains the product files and some starter application code. All you need to do is to fill in the missing parts.

  2. To inspect the repo that you cloned and go to the files that you need, run this command:

    cd node-essentials/nodejs-http/exercise-express-routing/reading-writing
    

    The outline of the directory should look like this:

    -| app.js
    -| client-get.js
    -| client-post.js
    -| client-put.js
    -| client-delete.js
    -| client-delete-route.js
    -| package.json
    
  3. The package.json file contains an express dependency. In the terminal, run the following command to install it:

    npm install
    

    npm reads from the dependencies section in package.json.

  4. Open app.js to inspect it. The file should look like this:

    const express = require('express')
    const app = express()
    const port = 3000
    
    let bodyParser = require('body-parser');
    app.use(bodyParser.json());
    
    let products = [];
    
    app.post('/products', function(req, res) {
      // implement
    });
    
    app.put('/products', function(req, res) {
      // implement
    });
    
    app.delete('/products/:id', function(req, res) {
      // implement
    });
    
    app.get('/products', (req, res) => {
      // implement
    })
    app.listen(port, () => console.log(`Example app listening on port ${port}!`));
    

    The app.js file shows a skeleton of a program. Your next job is to implement the routes.

Implement routes

To implement routes, add a little code and then test it. Do this method by method until you have a fully functional API.

  1. Support reading from the API. Locate the part of the code that looks like this:

    app.get('/products', (req, res) => {
      // implement
    })
    

    Replace it with this code:

    app.get('/products', (req, res) => {
      res.json(products);
    })
    
  2. To check that the code works, start the API by running this command:

    node app.js
    
  3. In a separate terminal, run this command:

    node client-get.js
    

    You should get the following output:

    Received data []
    Connection closed
    

The API responds with an empty array because you haven't written any data to it yet. Let's change that next.

Implement writing

  1. To implement writing, locate this code:

    app.post('/products', function(req, res) {
      // implement
    });
    

    Replace it with this code:

    app.post('/products', function(req, res) {
      const newProduct = { ...req.body, id: products.length + 1 }
      products = [ ...products, newProduct]
      res.json(newProduct);
    });
    

    The new code reads incoming data from req.body and constructs a JavaScript object from it. Next, it's added to the products array. Finally, the new product is returned to the user.

  2. To test the code, run the server program by running this command:

    node app.js
    
  3. In a separate terminal, run this command:

    node client-post.js
    

    You should see an output like this:

    response {"name":"product","id":1}
    Closed connection
    
  4. To check that the data is written to the API, run the following command:

    node client-get.js
    

    You should see the following output:

    Received data [{"name":"product","id":1}]
    Connection closed
    

    The response tells you that when you ran client-post.js, you wrote data to the API. Also, you ran client-get.js to query the API for data. The API responded with the data that you just wrote to it.

Implement the ability to update data

  1. To implement the ability to update your data, locate the code that looks like this:

    app.put('/products', function(req, res) {});
    

    Replace it with this code:

    app.put('/products', function(req, res) {
      let updatedProduct;
      products = products.map(p => {
        if (p.id === req.body.id) {
          updatedProduct = { ...p, ...req.body };
          return updatedProduct;
        }
        return p;
      })
      res.json(updatedProduct);
    });
    

    The new code locates the record in the products array that matches the id property, and it updates that record.

  2. To test the code, start the server application:

    node app.js
    
  3. In the other terminal, run this command to create a record:

    node client-post.js
    
  4. Run this command to update the newly created record:

    node client-put.js
    

    You should see the following update in the terminal:

    response {"name":"product-updated","id":1}
    Closed connection
    
  5. To check that the update works, run this command:

    node client-get.js
    

    You should see this update:

    Received data [{"name":"product-updated","id":1}]
    Connection closed
    

Implement deleting

  1. To implement deleting, locate the code that looks like this:

    app.delete('/products/:id', function(req, res) {});
    

    Replace it with this code:

    app.delete('/products/:id', function(req, res) {
      const deletedProduct = products.find(p => p.id === +req.params.id);
      products = products.filter(p => p.id !== +req.params.id);
      res.json(deletedProduct);
    });
    

    The new code finds the product item to be deleted. Then it filters out that item from the products array and responds with a filtered version of products.

  2. To test the code, start the server application:

    node app.js
    
  3. In a separate terminal, run this command to create a record:

    node client-post.js
    
  4. Run this command to remove the record:

    node client-delete.js
    

    You should see the following output:

    Received data {"name":"product","id":1}
    Connection closed
    
  5. To check the code, run this command:

    node client-get.js
    

    It should give this output:

    Received data []
    Connection closed
    

    Congratulations! You've implemented a products resource by using a full CRUD.

Implement CRUD

Implementing CRUD for a resource is a common task. Express has a route() method just for this purpose. When you use the route() method, your code is grouped so that it's easier to read.

  1. To implement CRUD, replace the code in app.js with this code:

    const express = require('express')
    const app = express()
    const port = 3000
    
    let bodyParser = require('body-parser');
    app.use(bodyParser.json());
    
    let products = [];
    
    app.route('/products')
     .get((req, res) => {
       res.json(products);
     })
     .post((req, res) => {
       const newProduct = { ...req.body, id: products.length + 1 }
       products = [...products, newProduct]
       res.json(newProduct);
     })
    .put((req, res) => {
       let updatedProduct;
       products = products.map(p => {
         if (p.id === req.body.id) {
           updatedProduct = { ...p, ...req.body };
           return updatedProduct;
         }
         return p;
       })
       res.json(updatedProduct);
     })
     .delete((req, res) => {
       const deletedProduct = products.find(p => p.id === +req.body.id);
       products = products.filter(p => p.id !== +req.body.id);
       res.json(deletedProduct);
     })
    
    app.listen(port, () => console.log(`Example app listening on port ${port}!`))
    
  2. To test this new implementation, restart the server by running this command:

    node app.js
    
  3. In a separate terminal window, run this command:

    node client-post.js
    
  4. Run this command to delete the record:

    node client-delete-route.js
    
  5. To check that the record is removed, run this command:

    node client-get.js
    

    You should see the following output:

    Received data []
    Connection closed
    

You used client-delete-route.js instead of client-delete.js in the previous exercise. The difference lies in how the route is implemented. The first version of app.js relies on deletions being done toward a route like /products/<id>, with the unique identifier being sent as a route parameter.

When you use the route() method, it implements the deletion route differently. It wants you to send unique identifier through the body instead of as a route parameter. There's no right or wrong way to implement a deletion route.