July 26

Full Stack: ToDo App with MySQL, Node.js, Express and React

So I had two goals for this summer, Build a full stack app and make a React Native app. Using a stack of MySQL, Node.js, Express and React I managed to build a full stack ToDo app! In this blog post I’ll show you how I did it!

The whole app works like this: I have a MySQL database where I store all my ToDos. Using Node.js and Express (a web application framework) I made a REST API that will allow my React front end to communicate with the MySQL database. And, of course, React as the front end.

Here’s how the React front end app ended up looking!

So, for the SQL server I could use either MySQL or MongoDB. I decided to use MySQL since I already have a lot of experience with it through school. MySQL is maintained by Oracle, and is both open-sourced and free to use! MySQL runs in the background of your computer and you can give it commands through the CMD to manage your database, but I strongly recommend downloading MySQL Workbench so you can get a nice GUI to use instead. I won’t go into depth about how I made my database here.

The ToDo table from MySQL Workbench

Now, for the API I decided to use Node.js and Express. They are also both free to use and very popular. For those that don’t know, Node.js is JavaScript runtime built using Chrome’s V8 JavaScript Engine. Basically, JavaScript usually only runs through a browser, but with Node.js we can run it without one. Express is a web app framework for Node.js. It makes creating APIs so much easier.

So first up is the entry point “server.js” file:

// Init
const express = require('express');
const bodyParser = require("body-parser");
const cors = require('cors');

// Set up a const for express and a port
const app = express();
const port = 3000;

// Init connection to database
const db = require('./db');

// Middleware
app.use(cors());
app.use(bodyParser.json());

// Routers
const todoRouter = require('./routes/todo.route');

// Use routers
app.use('/todo', todoRouter);

// Start listening/server
app.listen(port, () => console.log(`ToDo API listening on port ${port}...`));

First I import express itself, body-parser (used for taking the body information from a POST and making it accessible for the API) and CORS (which is used for being able to skip the Same-origin policy, which means we can connect with remote hosts to the API). Next is setting up express and a port to use with it. After that I set up something called “Middleware”. They are basically functions that gets called when the API receive a request and can manipulate the request object from the user and the response object from the API. We have to set both cors and body-parser as middleware for them to work. Next, I set up “routers” for my todos. I need to research routers a little more, but they let me organize my routes into a different file.

For connecting the API to my database I have a file called “db.config.js” which handles the settings:

module.exports = {
  HOST: "localhost",
  USER: "root",
  PASSWORD: "PASSWORD",
  DB: "tododb"
};

And then a “db.js” for connecting to the database:

const mysql = require("mysql");
const dbConfig = require("./config/db.config.js");

// Create a connection to the database
const connection = mysql.createConnection({
  host: dbConfig.HOST,
  user: dbConfig.USER,
  password: dbConfig.PASSWORD,
  database: dbConfig.DB
});

// open the MySQL connection
connection.connect(error => {
  if (error) throw error;
  console.log("Successfully connected to the database!...");
});

module.exports = connection;

Here I just create a connection using a MySQL library for Node.js, and export it so that the route files can use it.

Last up is the route file for ToDo. This is the code that’s gonna handle the HTTP request and responses for the ToDos. I’m not gonna show the entire file since it’s quite big, but all of the functions looks quite the same. It can be viewed through the GitHub page.

const express = require('express'),
    router = express.Router(),
    db = require('../db');

// Get one ToDo
router.get('/:todoId', function (req, res) {
    let sql = `SELECT * FROM todo WHERE id = ${req.params.todoId}`;

    db.query(sql, function (err, data, fields) {
        if (err) return res.status(400).send(err);

        if(Object.keys(data).length === 0) return res.status(404).send(`404: ToDo with id: ${req.params.todoId} not found.`);

        res.json({
            data,
            message: "ToDo retrieved successfully"
        })
    });
});

First, I’m importing express, the router and the db (database connection). I can now add functions that handles “Get”, “POST”, “Put” and “Delete” requests for my ToDos. In the code above I handle a Get request for getting a single specific ToDo from the database. Using “db” I can send a query to my database and use it in the callback function. From the callback function I can check if it’s empty (and in case return a HTTP status code 404: Not found) or send the data it found as a JSON object back to the React client.

And that’s it for the back-end. My API isn’t the best right now, but I’m planning to add JWT security and rewrite it so that it uses the information from the body of an HTTP request.

Now for the front-end. I’ve covered making React apps some times now, so for this blog post I’m only gonna focus on what’s related to the full-stack part.

First, in my constructor I add a var for my API Url:


  constructor(props) {
    super(props)

    this.apiURL = "http://localhost:3000";

    this.state = {
      todos: []
    }
  }
Then when my App starts I run a function called "getTodos" which sends a HTTP request to my API and (hopefully) receives some ToDos back, and sets the state:


// When the app starts, get todos from the database and set it to the state
  componentDidMount() {
    this.getTodos();
  }

  getTodos() {
    fetch(`${this.apiURL}/todo`)
      .then(res => res.json())
      .then((data) => {

        let todos = data.data;

        // Convert 'completed' from 0's to bools
        for (let i = 0; i < todos.length; i++) {
          todos[i].completed = (todos[i].completed === 0) ? false : true;
        }

        // Set todos in the state
        this.setState({ todos })
      })
      .catch(console.log)
  }

Since MySQL doesn't support straight up boolean values, I use 0's and 1's as false and true. That's why I convert the "completed" value from those 0's and 1's to bool's.

Now for adding a new ToDo:


  // Adds a new ToDo
  addToDo = desc => {
    fetch(`${this.apiURL}/todo?desc="${desc}"&completed=0`, { "method": "POST" })
      .then(res => res.json())
      .then((data) => {

        //Refresh ToDos
        this.getTodos();

      })
      .catch(console.log)

  }

I send a POST request with the new ToDos description in the query and a completed value of zero. The id is being set server side, so my client app doesn't need to set it itself. In the callback function I could receive the id back and just add a new ToDo object to my array, but I decided to take the lazy way and just refresh all ToDos from my database.

The delete and update (setAsComplete) functions are quite similiar:


// Marks a ToDo as completed
  setAsCompleted = id => {
    // Get the selected todo
    let editedToDo = this.state.todos.find(todo => todo.id === id);

    // Flip the completed value and return it as a number
    editedToDo.completed = editedToDo.completed ? 0 : 1;

    fetch(`${this.apiURL}/todo?id=${editedToDo.id}&desc="${editedToDo.desc}"&completed=${editedToDo.completed}`, { "method": "PUT" })
      .then(res => res.json())
      .then((data) => {

        //Refresh ToDos
        this.getTodos();

      })
      .catch(console.log)
  }

  // Removes ToDo
  removeToDo = id => {
    fetch(`${this.apiURL}/todo/${id}`, { "method": "DELETE" })
      .then(res => res.json())
      .then((data) => {

        //Refresh ToDos
        this.getTodos();

      })
      .catch(console.log)
  }

And that's it! A full working full-stack ToDo application!

This image has an empty alt attribute; its file name is image-8.png

My next plan is to port over my client React app to React Native, so stay tuned for that!



Copyright 2020. All rights reserved.

Posted July 26, 2020 by Atle in category "Full stack", "JavaScript", "ReactJS

Leave a Reply

Your email address will not be published. Required fields are marked *