React Redux & MySQL: CRUD example with Node.js Express

In this tutorial, I will show you how to build React Redux + MySQL CRUD example with Node.js Express server for REST APIs. Front-end side uses React Router, Axios & Bootstrap.

Related Posts:
React + Node.js Express: User Authentication with JWT example
React File Upload with Axios and Progress Bar to Rest API
Spring Boot + React Redux example: Build a CRUD App
– Front-end without Redux:

Run both projects in one place:
How to integrate React with Node.js Express on same Server/Port


React Redux + MySQL CRUD example Overview

We will build a full-stack Tutorial Application in that:

  • Tutorial has id, title, description, published status.
  • User can create, retrieve, update, delete Tutorials.
  • There is a search box for finding Tutorials by title.

Here are screenshots of the example.

– Add a Tutorial:

react-redux-mysql-crud-example-node-js-express-create-tutorial

– Show all tutorials:

react-redux-mysql-crud-example-node-js-express-retrieve-tutorial

– Click on Edit button to view details of an item:

react-redux-mysql-crud-example-node-js-express-retrieve-one-tutorial

On this Page, you can:

  • change status to Published/Pending using Publish/UnPublished button
  • remove the object from MySQL Database using Delete button
  • update this object’s details on Database with Update button

react-redux-mysql-crud-example-node-js-express-update-tutorial

– Search objects by field ‘title’:

react-redux-mysql-crud-example-node-js-express-search-tutorial

– Check MySQL database:

react-redux-mysql-crud-example-node-js-express-database-table

– Check Redux state with Dev-tool:

react-redux-mysql-crud-example-node-js-express-redux-state

React Redux + MySQL CRUD example Architecture

We’re gonna build the application with following architecture:

react-redux-mysql-crud-example-node-js-express-architecture

– Node.js Express exports REST APIs & interacts with MySQL Database using Sequelize ORM.
– React Client sends HTTP Requests and retrieves HTTP Responses using Axios, consume data on Redux which provides state to the Components. React Router is used for navigating to pages.

React Redux Front-end

Overview

This is React components that we’re gonna implement:

react-redux-mysql-crud-example-client-components

– The App component is a container with React Router. It has navbar that links to routes paths.

– Three components that dispatch actions to Redux Thunk Middleware which uses TutorialDataService to call Rest API.

  • TutorialsList component gets and displays Tutorials.
  • Tutorial component has form for editing Tutorial’s details based on :id.
  • AddTutorial component has form for submission new Tutorial.

TutorialDataService uses axios to make HTTP requests and receive responses.

This diagram shows how Redux elements work in our React Application:

react-redux-mysql-crud-example-node-js-express-store-architecture

We’re gonna create Redux store for storing tutorials data. Other React Components will work with the Store via dispatching an action.

The reducer will take the action and return new state.

Technology

  • React 17/16
  • react-redux 7.2.3
  • redux 4.0.5
  • redux-thunk 2.3.0
  • react-router-dom 5.2.0
  • axios 0.21.1
  • bootstrap 4

Project Structure

react-redux-mysql-crud-example-project-structure

package.json contains main modules: react, react-router-dom, react-redux, redux, redux-thunk, axios & bootstrap.
App is the container that has Router & navbar.
– There are 3 components: TutorialsList, Tutorial, AddTutorial.
http-common.js initializes axios with HTTP base Url and headers.
TutorialDataService has methods for sending HTTP requests to the Apis.
.env configures port for this React CRUD App.

About Redux elements that we’re gonna use:
actions folder contains the action creator (tutorials.js for CRUD operations and searching).
reducers folder contains the reducer (tutorials.js) which updates the application state corresponding to dispatched action.

If you want to work with React Hooks instead, please visit:
React Hooks + Redux: CRUD example with Axios and Rest API

Setup React Redux MySQL CRUD Project

Open cmd at the folder you want to save Project folder, run command:
npx create-react-app react-redux-crud-example

After the process is done. We create additional folders and files like the following tree:


public

src

actions

types.js

tutorials.js (create/retrieve/update/delete actions)

reducers

index.js

tutorials.js

components

add-tutorial.component.js

tutorial.component.js

tutorials-list.component.js

services

tutorial.service.js

App.css

App.js

index.js

store.js

package.json


Import Bootstrap to React Redux MySQL CRUD App

Run command: npm install bootstrap.

Open src/App.js and modify the code inside it as following-

import React, { Component } from "react";
import "bootstrap/dist/css/bootstrap.min.css";

class App extends Component {
  render() {
    // ...
  }
}

export default App;

Add React Router to React Redux MySQL CRUD App

– Run the command: npm install react-router-dom.
– Open src/App.js and wrap all UI elements by BrowserRouter object.

...
import { BrowserRouter as Router } from "react-router-dom";

class App extends Component {
  render() {
    return (
      <Router>
        ...
      </Router>
    );
  }
}

export default App;

Add Navbar to React Redux MySQL CRUD App

The App component is the root container for our application, it will contain a navbar inside <Router> above, and also, a Switch object with several Route. Each Route points to a React Component.

Now App.js looks like:

import React, { Component } from "react";
import { BrowserRouter as Router, Switch, Route, Link } from "react-router-dom";
import "bootstrap/dist/css/bootstrap.min.css";
import "./App.css";

import AddTutorial from "./components/add-tutorial.component";
import Tutorial from "./components/tutorial.component";
import TutorialsList from "./components/tutorials-list.component";

class App extends Component {
  render() {
    return (
      <Router>
        <nav className="navbar navbar-expand navbar-dark bg-dark">
          <Link to={"/tutorials"} className="navbar-brand">
            bezKoder
          </Link>
          <div className="navbar-nav mr-auto">
            <li className="nav-item">
              <Link to={"/tutorials"} className="nav-link">
                Tutorials
              </Link>
            </li>
            <li className="nav-item">
              <Link to={"/add"} className="nav-link">
                Add
              </Link>
            </li>
          </div>
        </nav>

        <div className="container mt-3">
          <Switch>
            <Route exact path={["/", "/tutorials"]} component={TutorialsList} />
            <Route exact path="/add" component={AddTutorial} />
            <Route path="/tutorials/:id" component={Tutorial} />
          </Switch>
        </div>
      </Router>
    );
  }
}

export default App;

Initialize Axios for React Redux with API calls

Let’s install axios with command: npm install axios.
Under src folder, we create http-common.js file with following code:

import axios from "axios";

export default axios.create({
  baseURL: "http://localhost:8080/api",
  headers: {
    "Content-type": "application/json"
  }
});

You can change the baseURL that depends on REST APIs url that your Server configures.

Create Data Service

In this step, we’re gonna create a service that uses axios object above to send HTTP requests or make API calls.

services/tutorial.service.js

import http from "../http-common";

class TutorialDataService {
  getAll() {
    return http.get("/tutorials");
  }

  get(id) {
    return http.get(`/tutorials/${id}`);
  }

  create(data) {
    return http.post("/tutorials", data);
  }

  update(id, data) {
    return http.put(`/tutorials/${id}`, data);
  }

  delete(id) {
    return http.delete(`/tutorials/${id}`);
  }

  deleteAll() {
    return http.delete(`/tutorials`);
  }

  findByTitle(title) {
    return http.get(`/tutorials?title=${title}`);
  }
}

export default new TutorialDataService();

We call axios get, post, put, delete method corresponding to HTTP Requests: GET, POST, PUT, DELETE to make CRUD Operations.

Create Redux Actions

We’re gonna create actions in src/actions folder:


actions

types.js

tutorials.js (create/retrieve/update/delete actions)


First we defined some string constant that indicates the type of action being performed.

actions/type.js

export const CREATE_TUTORIAL = "CREATE_TUTORIAL";
export const RETRIEVE_TUTORIALS = "RETRIEVE_TUTORIALS";
export const UPDATE_TUTORIAL = "UPDATE_TUTORIAL";
export const DELETE_TUTORIAL = "DELETE_TUTORIAL";
export const DELETE_ALL_TUTORIALS = "DELETE_ALL_TUTORIALS";

Next we make creators for actions related to tutorials. We’re gonna import TutorialDataService to make asynchronous HTTP requests with trigger dispatch on the result.

createTutorial()

  • calls the TutorialDataService.create()
  • dispatch CREATE_TUTORIAL

retrieveTutorials()

  • calls the TutorialDataService.getAll()
  • dispatch RETRIEVE_TUTORIALS

updateTutorial()

  • calls the TutorialDataService.update()
  • dispatch UPDATE_TUTORIAL

deleteTutorial()

  • calls the TutorialDataService.delete()
  • dispatch DELETE_TUTORIAL

deleteAllTutorials()

  • calls the TutorialDataService.deleteAll()
  • dispatch DELETE_ALL_TUTORIALS

findTutorialsByTitle()

  • calls the TutorialDataService.findByTitle()
  • dispatch RETRIEVE_TUTORIALS

Some action creators return a Promise for Components using them.

For more details, please visit:
React Redux CRUD example with Rest API

Create Redux Reducer

There will be a reducer in src/reducers folder, the reducer updates the state corresponding to dispatched Redux actions.


reducers

index.js

tutorials.js


The tutorials reducer will update tutorials state of the Redux store:
reducers/tutorials.js

import {
  CREATE_TUTORIAL,
  RETRIEVE_TUTORIALS,
  UPDATE_TUTORIAL,
  DELETE_TUTORIAL,
  DELETE_ALL_TUTORIALS,
} from "../actions/types";

const initialState = [];

function tutorialReducer(tutorials = initialState, action) {
  const { type, payload } = action;

  switch (type) {
    case CREATE_TUTORIAL:
      return [...tutorials, payload];

    case RETRIEVE_TUTORIALS:
      return payload;

    case UPDATE_TUTORIAL:
      return tutorials.map((tutorial) => {
        if (tutorial.id === payload.id) {
          return {
            ...tutorial,
            ...payload,
          };
        } else {
          return tutorial;
        }
      });

    case DELETE_TUTORIAL:
      return tutorials.filter(({ id }) => id !== payload.id);

    case DELETE_ALL_TUTORIALS:
      return [];

    default:
      return tutorials;
  }
};

export default tutorialReducer;

Because we only have a single store in a Redux application. We use reducer composition instead of many stores to split data handling logic.

reducers/index.js

import { combineReducers } from "redux";
import tutorials from "./tutorials";

export default combineReducers({
  tutorials,
});

Create Redux Store

This Store will bring Actions and Reducers together and hold the Application state.

Now we need to install Redux, Thunk Middleware and Redux Devtool Extension.
Run the command:

npm install redux react-redux redux-thunk
npm install --save-dev redux-devtools-extension

In the previous section, we used combineReducers() to combine 2 reducers into one. Let’s import it, and pass it to createStore():

store.js

import { createStore, applyMiddleware } from 'redux';
import { composeWithDevTools } from "redux-devtools-extension";
import thunk from 'redux-thunk';
import rootReducer from './reducers';

const initialState = {};

const middleware = [thunk];

const store = createStore(
  rootReducer,
  initialState,
  composeWithDevTools(applyMiddleware(...middleware))
);

export default store;

Provide State to React Components

We will use mapStateToProps and mapDispatchToProps to connect Redux state to React Components’ props later using connect() function:

export default connect(mapStateToProps, mapDispatchToProps)(ReactComponent);

So we need to make the Redux store available to the connect() call in the Components. We will wrap a parent or ancestor Component in Provider.

Provider is an high order component that wraps up React application and makes it aware of the entire Redux store. That is, it provides the store to its child components.

Now we want our entire React App to access the store, just put the App Component within Provider.

Open src/index.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
...
import { Provider } from 'react-redux';
import store from './store';

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
);

Create React Components/Pages

Now we’re gonna build 3 components corresponding to 3 Routes defined before.

  • Add new Item
  • List of items
  • Item details

You can continue with step by step to implement this React App in the post:
React Redux CRUD example with Rest API

Run React Redux Client

You can run our App with command: npm start.
If the process is successful, open Browser with Url: http://localhost:8081/ and check it.

Node.js Express Back-end

Overview

These are APIs that Node.js Express App will export:

MethodsUrlsActions
GETapi/tutorialsget all Tutorials
GETapi/tutorials/:idget Tutorial by id
POSTapi/tutorialsadd new Tutorial
PUTapi/tutorials/:idupdate Tutorial by id
DELETEapi/tutorials/:idremove Tutorial by id
DELETEapi/tutorialsremove all Tutorials
GETapi/tutorials?title=[kw]find all Tutorials which title contains 'kw'

Project Structure

react-redux-mysql-crud-example-node-js-server-project-structure

db.config.js exports configuring parameters for MySQL connection & Sequelize.
Express web server in server.js where we configure CORS, initialize & run Express REST APIs.
– Next, we add configuration for MySQL database in models/index.js, create Sequelize data model in models/tutorial.model.js.
– Tutorial controller in controllers.
– Routes for handling all CRUD operations (including custom finder) in tutorial.routes.js.

Create Node.js App

First, we create a folder:

$ mkdir nodejs-express-sequelize-mysql
$ cd nodejs-express-sequelize-mysql

Next, we initialize the Node.js App with a package.json file:

npm init

name: (nodejs-express-sequelize-mysql) 
version: (1.0.0) 
description: Node.js Rest Apis with Express, Sequelize & MySQL.
entry point: (index.js) server.js
test command: 
git repository: 
keywords: nodejs, express, sequelize, mysql, rest, api
author: bezkoder
license: (ISC)

Is this ok? (yes) yes

We need to install necessary modules: express, sequelize, mysql2 and body-parser.
Run the command:

npm install express sequelize mysql2 body-parser cors --save

Setup Express web server

In the root folder, let’s create a new server.js file:

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

const app = express();

var corsOptions = {
  origin: "http://localhost:8081"
};

app.use(cors(corsOptions));

// parse requests of content-type - application/json
app.use(bodyParser.json());

// parse requests of content-type - application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: true }));

// simple route
app.get("/", (req, res) => {
  res.json({ message: "Welcome to bezkoder application." });
});

// set port, listen for requests
const PORT = process.env.PORT || 8080;
app.listen(PORT, () => {
  console.log(`Server is running on port ${PORT}.`);
});

What we do are:
– import express, body-parser and cors modules:

  • Express is for building the Rest apis
  • body-parser helps to parse the request and create the req.body object
  • cors provides Express middleware to enable CORS with various options.

– create an Express app, then add body-parser and cors middlewares using app.use() method. Notice that we set origin: http://localhost:8081.
– define a GET route which is simple for test.
– listen on port 8080 for incoming requests.

Now let’s run the app with command: node server.js.
Open your browser with url http://localhost:8080/, you will see:

node-js-express-sequelize-mysql-example-setup-server

Yeah, the first step is done. We’re gonna work with Sequelize in the next section.

Configure MySQL database & Sequelize

In the app folder, we create a separate config folder for configuration with db.config.js file like this:

module.exports = {
  HOST: "localhost",
  USER: "root",
  PASSWORD: "123456",
  DB: "testdb",
  dialect: "mysql",
  pool: {
    max: 5,
    min: 0,
    acquire: 30000,
    idle: 10000
  }
};

First five parameters are for MySQL connection.
pool is optional, it will be used for Sequelize connection pool configuration:

  • max: maximum number of connection in pool
  • min: minimum number of connection in pool
  • idle: maximum time, in milliseconds, that a connection can be idle before being released
  • acquire: maximum time, in milliseconds, that pool will try to get connection before throwing error

For more details, please visit API Reference for the Sequelize constructor.

Initialize Sequelize

We’re gonna initialize Sequelize in app/models folder that will contain model in the next step.

Now create app/models/index.js with the following code:

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

const Sequelize = require("sequelize");
const sequelize = new Sequelize(dbConfig.DB, dbConfig.USER, dbConfig.PASSWORD, {
  host: dbConfig.HOST,
  dialect: dbConfig.dialect,
  operatorsAliases: false,

  pool: {
    max: dbConfig.pool.max,
    min: dbConfig.pool.min,
    acquire: dbConfig.pool.acquire,
    idle: dbConfig.pool.idle
  }
});

const db = {};

db.Sequelize = Sequelize;
db.sequelize = sequelize;

db.tutorials = require("./tutorial.model.js")(sequelize, Sequelize);

module.exports = db;

Don’t forget to call sync() method in server.js:

...
const app = express();
app.use(...);

const db = require("./app/models");
db.sequelize.sync();

...

In development, you may need to drop existing tables and re-sync database. Just use force: true as following code:


db.sequelize.sync({ force: true }).then(() => {
  console.log("Drop and re-sync db.");
});

Define the Sequelize Model

In models folder, create tutorial.model.js file like this:

module.exports = (sequelize, Sequelize) => {
  const Tutorial = sequelize.define("tutorial", {
    title: {
      type: Sequelize.STRING
    },
    description: {
      type: Sequelize.STRING
    },
    published: {
      type: Sequelize.BOOLEAN
    }
  });

  return Tutorial;
};

This Sequelize Model represents tutorials table in MySQL database. These columns will be generated automatically: id, title, description, published, createdAt, updatedAt.

After initializing Sequelize, we don’t need to write CRUD functions, Sequelize supports all of them:

  • create a new Tutorial: create(object)
  • find a Tutorial by id: findByPk(id)
  • get all Tutorials: findAll()
  • update a Tutorial by id: update(data, where: { id: id })
  • remove a Tutorial: destroy(where: { id: id })
  • remove all Tutorials: destroy(where: {})
  • find all Tutorials by title: findAll({ where: { title: ... } })

These functions will be used in our Controller.

We can improve the example by adding Comments for each Tutorial. It is the One-to-Many Relationship and I write a tutorial for this at:
Sequelize Associations: One-to-Many example – Node.js, MySQL

Or you can add Tags for each Tutorial and add Tutorials to Tag (Many-to-Many Relationship):
Sequelize Many-to-Many Association example with Node.js & MySQL

Create the Controller

Inside app/controllers folder, let’s create tutorial.controller.js with these CRUD functions:

  • create
  • findAll
  • findOne
  • update
  • delete
  • deleteAll
  • findAllPublished
const db = require("../models");
const Tutorial = db.tutorials;
const Op = db.Sequelize.Op;

// Create and Save a new Tutorial
exports.create = (req, res) => {
  
};

// Retrieve all Tutorials from the database.
exports.findAll = (req, res) => {
  
};

// Find a single Tutorial with an id
exports.findOne = (req, res) => {
  
};

// Update a Tutorial by the id in the request
exports.update = (req, res) => {
  
};

// Delete a Tutorial with the specified id in the request
exports.delete = (req, res) => {
  
};

// Delete all Tutorials from the database.
exports.deleteAll = (req, res) => {
  
};

// Find all published Tutorials
exports.findAllPublished = (req, res) => {
  
};

You can continue with step by step to implement this Node.js Express App in the post:
Node.js Rest APIs example with Express, Sequelize & MySQL

Run the Node.js Express Server

Run our Node.js application with command: node server.js.
The console shows:

Server is running on port 8080.
Executing (default): DROP TABLE IF EXISTS `tutorials`;
Executing (default): CREATE TABLE IF NOT EXISTS `tutorials` (`id` INTEGER NOT NULL auto_increment , `title` VARCHAR(255), `description` VARCHAR(255), `published` TINYINT(1), `createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB;
Executing (default): SHOW INDEX FROM `tutorials`
Drop and re-sync db.

Conclusion

Today we have an overview of React Redux + MySQL example when building a full-stack with Nodejs Express backend.

We also take a look at client-server architecture for REST API using Express & Sequelize ORM, as well as React Redux project structure for building a front-end app to make HTTP requests and consume responses.

Next tutorials show you more details about how to implement the system (including source code):
Back-end
– Front-end:

You will want to know how to run both projects in one place:
How to integrate React with Node.js Express on same Server/Port

With Pagination:
React Pagination with API using Material-UI

react-pagination-with-api-material-ui-change-page

Or Serverless with Firebase:
React Firebase CRUD with Realtime Database
React Firestore CRUD App example | Firebase Cloud Firestore

– Front-end without Redux:

– Spring Boot Back-end:
Spring Boot + React Redux example: Build a CRUD App

Happy learning, see you again!

Further Reading

Leave a Reply

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