Create a CRUD API on Express and MySQL: Part Two

Hello everyone. In anticipation of the start of the course "Developer Node.js" , we want to share the continuation of the material that was written by our freelance author.





Hello again to everyone. We return to creating an application on Node.js and MySQL for a small todo - application on Node.js for React. Since the last time, I revised the structure of our application a bit, and now I decided to add an additional column to the database under the name inner_key, which we will need to draw unique keys for each individual case (in the list of React repeating elements, we need a unique key for each element to process it in Virtual DOM. If this still raises questions for you, you should study this article ).



We can add a column using the following command in MySQL:

  ALTER TABlE TODO ADD inner_key varchar(100);

Yes, it’s probably not worth it timestamp(namely, with the help of which Date.now()I create the keys for my business in React) put in a column varchar, but I hope that it will not be difficult to fix it if someone will of course optimize our application. On the other hand, I am not going to use timestamp to work with time in my application, I only need a unique value. So for now, this is not a problem.

Minor updates to our model


Further, in connection with these changes, the very model of our application has changed.
Now our instance constructor at the beginning of our model looks different; it has a new property:

const Deal = function(deal) {
  this.text = deal.text;
  this.inner_key = deal.inner_key;
};


I changed the operation to create a case in Model purely cosmetically. But the operations of getting the case on idhad to be changed quite a lot, because now we are getting the case on inner_key. The only thing is, I did not begin to change the parameters res dealId, but in principle, this does not greatly cut the readability of the code:

Deal.findById = (dealId, result) => {
  sql.query(`SELECT * FROM TODO WHERE inner_key = '${dealId}'`, (err, res) => {

	//   ,     

    if (res.length) {
      console.log(" : ", res[0]);
      result(null, res[0]);
      return;
    }
    //     
    result({ kind: "not_found" }, null);
  });
};

In addition to it, a request for all the data from the table underwent a small mutation. For my React - applications are not needed idfrom the database, I need inner_key. Therefore, the request itself has changed a bit:

Deal.getAll = result => {
   const queryAll = "SELECT text, inner_key FROM TODO";
  sql.query(queryAll, (err, res) => {
//  

In the update method, we also now update the case by innerkey, and the same thing happens in the delete method:

Deal.updateById = (inner_key, deal, result) => {
  const queryUpdate = "UPDATE TODO SET text = ? WHERE inner_key = ?";
  sql.query(
    queryUpdate,
    [deal.text, inner_key],
    (err, res) => {

//  
      }
      //  
     //   
  const queryDelete = "DELETE FROM TODO WHERE inner_key = ?";
  sql.query(queryDelete, inner_key, (err, res) => {
	//  
    }

As you noticed, I have incomplete code here, because even curly brackets do not close, but I don’t see the full code to give the full code. If, after all, something is not clear, then you can see it here .

Creating Your Controller


In the controller, we export the functions that we created in the model. If in general, then each time we validate the request by checking whether an empty body has been sent (if you plan to transform the API into an open one, then it must have, and indeed it is useful). Further actions differ depending on whether you need to read res.idto complete the request, or if this is not a refined request. In the method, findAllI left resolving all response headers in order to simplify testing my API.

    
const Deal = require("../models/deal.model.js");

//    
exports.create = (req, res) => {
  //   
  if (!req.body) {
    res.status(400).send({
      message: "      "
    });
  }

  //   

  const deal = new Deal({
    text: req.body.text,
    inner_key: req.body.inner_key
    //        id,     
    //     React
  });


  Deal.create(deal, (err, data) => {
    if (err)
      res.status(500).send({
        message:
          err.message || "     "
      });
    else res.send(data);
  });
};

//      
exports.findAll = (req, res) => {
  Deal.getAll((err, data) => {
    if (err)
      res.status(500).send({
        message:
          err.message || "-      "
      });
    else 
    res.setHeader('Access-Control-Allow-Origin', '*');
    res.setHeader('Access-Control-Allow-Headers', 'origin, content-type, accept');
    //   ,   ,   ,          origin
    //     open API -,    
    res.send(data);
  });
};

I will cram the rest of the code under the spoiler , because we will only test the above functions. However, the rest of the code is also working, but it is technically different only in arguments from what has already been voiced:

the rest of the code
//       inner_id
exports.findOne = (req, res) => {
  Deal.findById(req.params.dealId, (err, data) => {
    if (err) {
      if (err.kind === "not_found") {
        res.status(404).send({
          message: `   id ${req.params.dealId}.`
        });
      } else {
        res.status(500).send({
          message: "     id" + req.params.dealId
        });
      }
    } else res.send(data);
  });
};

//    inner_id
exports.update = (req, res) => {
  //  
  if (!req.body) {
    res.status(400).send({
      message: "    "
    });
  }

//    "" -    inner_key
  Deal.updateById(
    req.params.dealId,
    new Deal(req.body),
    (err, data) => {
      if (err) {
        if (err.kind === "not_found") {
          res.status(404).send({
            message: `    id ${req.params.dealId}.`
          });
        } else {
          res.status(500).send({
            message: "Error updating deal with id " + req.params.dealId
          });
        }
      } else res.send(data);
    }
  );
};

//    inner_key
exports.delete = (req, res) => {
  Deal.remove(req.params.dealId, (err, data) => {
    if (err) {
      if (err.kind === "not_found") {
        res.status(404).send({
          message: `    ${req.params.dealId}.`
        });
      } else {
        res.status(500).send({
          message: "     " + req.params.dealId
        });
      }
    } else res.send({ message: `   ` });
  });
};

//     
exports.deleteAll = (req, res) => {
  Deal.removeAll((err, data) => {
    if (err)
      res.status(500).send({
        message:
          err.message || "-        "
      });
    else res.send({ message: `   ` });
  });
};

  


Routing


Now let's move on to the sweetest and easiest: a description of the routing, which you must then remember to import later into server.js itself. Using the described ways and methods, we will be able to receive and add data. In our app folder, create a routes subfolder in which we need a file deals.routes.js. In it you need to write the following:

module.exports = app => {
 //  ,         

    const deals = require("../controllers/deal.controller.js");
  
    //      post
    app.post("/deals", deals.create);
  
    //    
    app.get("/deals", deals.findAll);
  
    //    id (      inner_key),      
    app.get("/deal/:dealId", deals.findOne);
  
    //    id
    //     inner_key
    app.put("/deal/:dealId", deals.update);
  
    //   id
    app.delete("/deal/:dealId", deals.delete);
  
    //    
    app.delete("/deals", deals.deleteAll);
  };
 

After that, open our file server.jsand add the following over the wiretap of the port:

  require("./app/routes/deals.routes.js")(app);

 

Directly using our API


Most of these articles end up testing the API using POSTMAN . This is really a great tool that you should master if you plan to at least do a little professional development of the API (and it is not so important for which platform and in which language). If you have finished writing your application, and you forgot to turn on your database, now you can start the application itself:

<node server.js>

If you are completely new, then you will be interested to hear about a package nodemonthat restarts your application if there have been changes in the project files:

  npm i nodemon -g
  nodemon server.js

Now it will be much easier to debug errors that occur. But we were going to test this in a real React application.

I have at hand a simple react-todo that an attentive reader may have noticed in the deployment article of the react application on Heroku of this blog. However, I will not rewrite the entire back-end to our react-application (although this is not at all difficult, but I do not want to over-lengthen the article). Therefore, I will run the React application on the embedded servercreate-react-appunder port 3000, and our application runs under port 5003. Now let the difficult CRS stand in our way, we can easily get our data at least all todo. I almost completely took an example of the work of our requests from the official React documentation, which is devoted to fetch requests. Requests in React applications are usually carried out after rendering the component in componentDidMount, and it is in it that you need to make requests to remote resources:

   class App extends Component{
    constructor(){
      super()
      this.state ={
        error: null,
        isLoaded: false,
        items:[],
        currentItem: {text:" ", inner_key:"firstItem"}
        
      }
    }
    componentDidMount() {
      fetch("http://localhost:5003/deals")
        .then(res => res.json())
        .then(
          (result) => {
            this.setState({
              //   ,     
              isLoaded: true,
              items: result
            });
          },
          // :     ,     catch(),
          //         .
          (error) => {
            this.setState({
              isLoaded: true,
              error
            });
          }
        )
    }

Now we have things to do that are pulled from the database:



Everything works in the application itself:



Our application works to get all the cases. However, the React application has not yet described methods for either deleting or adding cases. To simplify cors requests, you should add a React js-build service in the node application, and then describe the methods for adding cases by inner_id, deleting them and updating them.

Thank you all for your attention. By tradition, some useful links:

A little interesting from the express documentation about the operation of middleware, in particular body-express
Writing asynchronous requests for React without axios is no longer fashionable at all.
And a little bit about setting up cors in Express, since it was discussed

All Articles