React, MySQL, Express Summary

  1. Work with Postman.
    1. Create Collection Mission3 .
    2. Create Get request
      localhost:5000/api/items
    3. Create Post request
      localhost:5000/api/items
      • Select POST request
      • Body Type: raw
      • Select JSON
      • Body Content:
          {
            "name": "test",
            "status": true
          }
                      

  2. Open VC Code for this project only.
  3. Create project mission3 in VS Code.
  4. Put .gitignore file into this folder.
  5. Create myNotes.txt file in this folder.

  6. React
    1. Install React
      npx create-react-app client
      OR
      npx create-react-app . --use-npm
      cd client
         npm i react-router-dom
    2. Clean files.
    3. In package.json in client folder add
      "proxy": "http://localhost:5000"
    4. Add Bootstrap CSS
      • In public/index.html
            <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous">
          
      • OR
      • In App.css at the top write
          npm i bootstrap
          @import "bootstrap/dist/css/bootstrap.min.css";
        
    5. Font Awesome
        cd my-app
        npm i @fortawesome/fontawesome-free
      
        In App.css
        @import "@fortawesome/fontawesome-free/css/all.min.css";
      

  7. Install Server Side packages
    Create server.js file.
    npm init -y
    npm i express mysql
    npm i -D nodemon concurrently

    Update package.json file

            "scripts": {
              "start": "node server.js",
              "server": "nodemon server.js --ignore ./client",
              "client": "npm start --prefix client",
              "dev": "concurrently \"npm run server\" \"npm run client\""
            },
          

  8. Work with MySQL Workbench
    1. Create database
      CREATE DATABASE mission3;
           CREATE USER 'johnBryce_user'@'localhost' IDENTIFIED BY 'myPassword';
           SELECT * FROM mysql.user;
      GRANT ALL PRIVILEGES ON mission3.* TO 'johnBryce_user'@'localhost';
      FLUSH PRIVILEGES;
    2. Create Tables
      •     CREATE TABLE IF NOT EXISTS mission3.companies (
              id INT AUTO_INCREMENT PRIMARY KEY,
              companyName CHAR(100) NOT NULL
              );
                      
      •     CREATE TABLE mission3.servers (
              id INT AUTO_INCREMENT PRIMARY KEY,
              serverName CHAR(100) NOT NULL,
              ip CHAR(255) NOT NULL,
              hostCompId INT NOT NULL,
              status BOOLEAN DEFAULT FALSE,
              createdAt TIMESTAMP DEFAULT NOW(),
              FOREIGN KEY (hostCompId) REFERENCES mission3.companies(id)
            );
                      
      •   CREATE TABLE IF NOT EXISTS mission3.products (
            id INT AUTO_INCREMENT PRIMARY KEY,
            name CHAR(100) NOT NULL,
            price INT NOT NULL,
            vendorId INT NOT NULL,
            units INT NOT NULL DEFAULT 0,
            updatedAt TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,   
            FOREIGN KEY (vendorId) REFERENCES mission3.vendors(id)
          );                
                      
    3. Init Tables

      •     INSERT INTO mission3.companies 
              (companyName)
              VALUES 
                ('IBM'),
                ('Microsoft'),
                ('Google'),
                ('DigitalO');
                        
      •       INSERT INTO mission3.servers 
                (serverName, ip, hostCompId)
                VALUES 
                  ('Server A', '1.1.1.1', 1),
                  ('Server B', '2.2.2.2', 2),
                  ('Server C', '3.3.3.3', 3),
                  ('Server D', '4.4.4.4', 4);                
            
      •     INSERT INTO mission3.servers 
              (serverName, ip, hostCompId, status, createdAt)
              VALUES 
                ('Server F', '10.116.093.192',  3,  1, "2010-01-31 19:00:00"),
                ('Server G', '10.010.093.102',  4,  0, "2016-02-29 18:00:00"),
                ('Server H', '10.100.100.100',  3,  1, "2019-03-31 20:00:00"),
                ('Server T', '10.170.193.132',  1,  0, "2018-04-30 19:30:00");
                      
    4. Run queries in Workbench
      • GET
            SELECT s.id, s.serverName, s.ip, s.hostCompId, s.status, s.createdAt, c.id, c.companyName
            FROM mission3.servers AS s
            LEFT JOIN mission3.companies AS c
            ON s.hostCompId = c.id
                      
      • INSERT

        See above

      • UPDATE
            UPDATE mission3.servers 
            SET status = true
            WHERE id = 1
                      
      • DELETE
            DELETE 
            FROM mission3.tasks 
            WHERE id = 7;              
                      

      • More
        • CREATE
          • Example #1
              CREATE TABLE users (
              id varchar(20) NOT NULL,
              firstName varchar(25) NOT NULL,
              lastName varchar(25) NOT NULL,
              email varchar(70) NOT NULL,
              password varchar(70) NOT NULL,
              city varchar(25) NOT NULL,
              street varchar(25) NOT NULL,
              house varchar(15) NOT NULL,
              apartment int NOT NULL,
              role ENUM('user', 'admin') DEFAULT 'user', 
              createdAt TIMESTAMP DEFAULT NOW(),
              updatedAt TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,   
              PRIMARY KEY (id),
              UNIQUE KEY id (id),
              UNIQUE KEY email (email)
            );
            
          • Example #2
                CREATE TABLE IF NOT EXISTS categories (
                id int AUTO_INCREMENT NOT NULL,
                name VARCHAR(30) NOT NULL,
                PRIMARY KEY (id)
              );
            
          • Example #3
              CREATE TABLE IF NOT EXISTS products (
                id int AUTO_INCREMENT NOT NULL,
                name VARCHAR(25) NOT NULL,
                price DECIMAL(12,2) NOT NULL,
                imagePath VARCHAR(200) NOT NULL,
                categoryId int NOT NULL,   
                PRIMARY KEY (id),
                FOREIGN KEY (categoryId) REFERENCES categories(id)
              );
            
          • Example #4
            CREATE TABLE carts (
              id int NOT NULL AUTO_INCREMENT,
              userId varchar(20) NOT NULL,
              isOpen BOOLEAN DEFAULT TRUE,
              createdAt TIMESTAMP DEFAULT NOW(),
              PRIMARY KEY (id),
              FOREIGN KEY (userId) REFERENCES users(id)
            );
            
          • Example #5
            CREATE TABLE cartItems (
              id int NOT NULL AUTO_INCREMENT,
              productId int NOT NULL,
              quantity int NOT NULL,
              itemPrice DECIMAL(12,2) NOT NULL,
              cartId int NOT NULL,
              PRIMARY KEY (id),
              FOREIGN KEY (productId) REFERENCES products(id),
              UNIQUE KEY `unique_keys` (cartId, productId),
              FOREIGN KEY (cartId) REFERENCES carts(id) 
            );
            
          • Example #6
            CREATE TABLE orders (
              id int NOT NULL AUTO_INCREMENT,
              totalPrice DECIMAL(20,2) NOT NULL,
              userId varchar(20) NOT NULL,
              cartId int NOT NULL,
              city varchar(25) NOT NULL,
              street varchar(25) NOT NULL,
              house varchar(15) NOT NULL,
              apartment int NOT NULL,
              dateToShip DATE NOT NULL,
              creditCardPartialDigits varchar(4) NOT NULL, 
              createdAt TIMESTAMP DEFAULT NOW(),
              PRIMARY KEY (id),
              FOREIGN KEY (userId) REFERENCES users (id),
              FOREIGN KEY (cartId) REFERENCES carts (id)
            );
            
          • Example #7
            
            

        • SELECT
          • Example #1
                SELECT * FROM people
                WHERE id IN
                (SELECT personId FROM people_in_films 
                WHERE filmId = 123)
            
          • Example #2
              SELECT c.name 
              FROM jbh_shop.customers AS c 
              LEFT JOIN jbh_shop.orders AS o ON c.id = o.customer_id 
              LEFT JOIN jbh_shop.orders_products AS op ON o.id = op.order_id 
              LEFT JOIN jbh_shop.products AS p ON op.product_id = p.id 
              WHERE p.name LIKE "Apple%"
            
          • Example #3
              SELECT c.id, c.name 
              FROM jbh_shop.customers AS c 
              INNER JOIN 
                (SELECT o.customer_id 
                  FROM jbh_shop.orders AS o 
                  INNER JOIN jbh_shop.orders_products AS op1 ON o.id = op1.order_id 
                  INNER JOIN jbh_shop.orders_products AS op2 ON o.id = op2.order_id
                  WHERE op1.product_id = 1 
                    AND op2.product_id = 1 
                  GROUP BY o.customer_id ) 
                AS t ON c.id = t.customer_id
            
          • Example #4
              SELECT c.id, c.name, 
              Count(op.product_id) 
              FROM jbh_shop.customers AS c 
              LEFT JOIN jbh_shop.orders AS o ON c.id = o.customer_id 
              LEFT JOIN jbh_shop.orders_products AS op ON o.id = op.order_id 
              GROUP BY order_id
            
          • Example #5
              SELECT 
              ROUND(AVG(totalPricePerOrder)) 
              FROM 
                ( SELECT SUM(p.price) AS totalPricePerOrder 
                FROM jbh_shop.orders_products AS op 
                LEFT JOIN jbh_shop.products AS p ON op.product_id = p.id 
                LEFT JOIN jbh_shop.orders AS o ON o.id = op.order_id 
                LEFT JOIN jbh_shop.customers AS c ON c.id = o.customer_id 
                GROUP BY op.order_id ) 
              AS inner_query
            
          • Example #6
              SELECT id, first_name FROM johnbryceusers.users 
              WHERE date_of_birth IN 
                (SELECT MAX(date_of_birth) 
                FROM johnbryceusers.users 
                WHERE date_of_birth < 
                  (SELECT 
                    MAX(date_of_birth) 
                    FROM johnbryceusers.users
                  )
                );
            
          • Example #7
              SELECT COUNT(*) 
              FROM johnbryceusers.users 
              WHERE email LIKE '%edu';
            
          • Example #8
              SELECT first_name,last_name 
              FROM johnbryceusers.users 
              WHERE date_of_birth='2000';
            
          • Example #9
              SELECT 
              ROUND(AVG(date_of_birth)) 
              FROM johnbryceusers.users 
              WHERE phone LIKE '050%';
            
          • Example #10
              SELECT date_of_birth 
              FROM johnbryceusers.users 
              GROUP BY date_of_birth 
              ORDER BY Count(id) 
              DESC 
              LIMIT 3;
            
          • Example #11
            
            

  9. Server Side
    1. database/db.js file
                  const mysql = require('mysql');
      
                  const pool = mysql.createPool({
                    connectionLimit: 10,
                    host: 'localhost',
                    user: 'johnBryce_user',
                    password: 'myPassword',
                    database: 'mission3'
                  });
                  
                  pool.on('connection', () => console.log('Mysql connection'));
                  /* pool.query('SELECT * FROM servers', (err, res) => {
                    if (err) throw err;
                    console.log(res);
                  }); */
                  
                  module.exports = { pool };            
                

    2. server.js file
                  const express = require('express');
                  const { pool } = require('./database/db');
                  const app = express();
      
                  app.use(express.json());
                  
                  // GET
                  app.get('/api/items', (req, res) => {            
                    res.status(200).json({});
                  });
      
                  // POST
                  app.post('/api/items', (req, res) => {
                    res.status(200).json({});
                  });
      
                  const PORT = process.env.PORT || 5000;
                  app.listen(PORT, console.log(`Server started on port ${PORT}`));
                

    3. Additional suggestion for server.js file
                  const express = require('express');
                  const cors = require('cors');
                  const { randomBytes } = require('crypto');
                  require('dotenv').config();
      
                  const app = express();
                
                  // Body Parser Middleware
                  app.use(express.json());
      
                  app.use(cors());
      
                  const posts = {};
                  const baseUrl = process.env.BASE_URL;
      
                  app.get(`${baseUrl}posts`, (req, res) => {
                    res.send(posts);
                  });
      
                  app.post(`${baseUrl}posts`, (req, res) => {
                    const id = randomBytes(4).toString('hex');
                    const { title } = req.body;
      
                    posts[id] = {
                      id,
                      title,
                    };
      
                    res.status(201).send(posts[id]);
                  });
      
                  const PORT = process.env.PORT || 4000;
                  app.listen(PORT, () => {
                    console.log(`Server Started in ${baseUrl} on Port ${PORT}`);
                  });
                

    4. GET
                  // GET
                  app.get('/api/servers', (req, res) => {
                    const sql = `
                      SELECT s.id, s.serverName, s.ip, s.hostCompId, s.status, s.createdAt, c.id, c.companyName
                      FROM servers AS s
                      LEFT JOIN companies AS c
                      ON s.hostCompId = c.id
                    `;
                    pool.query(sql, (err, results) => {
                      if (err) {
                        res.status(500).json({success: false});
                        throw err;
                      }
                      res.status(200).json({success: true, data: results});
                    });  
                  });
                

    5. POST
                  // POST
                  app.post('/api/todolist', (req, res) => {
                    const { taskName, ownerId } = req.body;
                  
                    //Validation
                    if (isNaN(Number(ownerId)) || !taskName) {
                      return res.status(400).json({success: false});
                    }
                  
                    const newTask = { taskName, ownerId };
                  
                    const sql = `
                      INSERT INTO tasks 
                      SET ?
                    `;
                    pool.query(sql, [newTask], (err, results, fields) => {
                      if (err) {
                        res.status(400).json({success: false});
                        throw err;
                      }
                      res.status(200).json({success: true});
                    })
                  });            
                

    6. PUT
                  // PUT
                  app.put('/api/server/status', (req, res) => {
      
                    const { status, id } = req.body;
                  
                    // Validation
                    if (isNaN(Number(id)) || ![true, false].includes(status)) {
                      console.log('Validation has failed.');
                      return res.status(400).json({success: false});
                    }
                  
                    const sql = `
                      UPDATE servers
                      SET status = ?
                      WHERE id = ?
                    `;
                  
                    pool.query(sql, [status, id],  (err, results) => {
                      if (err) {
                        res.status(500).json({success: false});
                        throw err;
                      }
                      res.status(200).json({success: true});
                    });
                  });            
                

    7. DELETE
                    //DELETE
                    app.delete('/api/todolist/:id', (req, res) => {
      
                      const taskId = req.params.id;
                    
                      //Validation
                      if (isNaN(Number(taskId))) {
                        return res.status(400).json({success: false});
                      }
                    
                      const sql = `
                        DELETE 
                        FROM tasks 
                        WHERE id = ?;
                      `;
                    
                      pool.query(sql, [taskId], (err, results, fields) => {
                        if (err) {
                          res.status(400).json({success: false});
                          throw err;
                        }
                        res.status(200).json({success: true});
                      });
                    });              
                  

  10. Client Side
    1. Context
      • context/types.js
        export const GET_SERVERS = 'GET_SERVERS';
        export const CHANGE_SERVER_STATUS = 'CHANGE_SERVER_STATUS';
                      
      • context/servers/serversContext.js
        import { createContext } from 'react';
        
        const serversContext = createContext();
        
        export default serversContext;               
                      
      • context/servers/serversReducer.js
        import { GET_SERVERS, CHANGE_SERVER_STATUS } from '../types';
        
        export default (state, action) => {
          const { type, payload } = action;
          const { servers } = state;
        
          switch(type) {
            case GET_SERVERS:
              return {
                ...state,
                servers: payload
              }
            case CHANGE_SERVER_STATUS:
              const { id } = payload;
        
              return {
                ...state,
                servers: servers.map(server => 
                  server.id === id 
                    ? { ...server, status: server.status === 1 ? 0 : 1}
                    : server
                  )
              }
              ...
              case UPDATE_UNITS:
              const { id, units } = payload;
              return {
                ...state,
                products: products.map(product => 
                  product.id === id 
                    ? { ...product, units: units, updatedAt: Date.now() }
                    : product
                  )
              }      
              ...
            default:
              return state;
          }
        }                
          
      • context/servers/ServersProvider.js
          import React, { useReducer } from 'react';
          import serversContext from './serversContext';
          import serversReducer from './serversReducer';
          import { GET_SERVERS, CHANGE_SERVER_STATUS } from '../types';
          
          const ServersProvider = props => {
            const initialState = {
              servers: []
            }
          
            const [state, dispatch] = useReducer(serversReducer, initialState);
          
            const getServers = async () => {
            ...
            }
          
            const changeServerStatus = async (status, id) => {
            ...
            }
          
            const { servers } = state;
          
            return (
              <serversContext.Provider 
                value={{
                  servers,
                  getServers,
                  changeServerStatus
                }}
              >
                {props.children}
              </serversContext.Provider>
            )
          }
          
          export default ServersProvider;               
                      

      • App.js
        import ServersProvider from './context/servers/ServersProvider';
        
        function App() {
          return (
            <ServersProvider>
        
            </ServersProvider>
          );
        }                
                      

      • GET
         
          const getServers = async () => {
            const url = '/api/servers';
        
            try {
              const res = await fetch(url);
              const response = await res.json();
        
              console.log(response.data);
        
              if (res.status === 200) {
                dispatch({
                  type: GET_SERVERS,
                  payload: response.data
                })
              } else {
                  alert('Error');
              }
            } catch(err) {
                alert(err);
            }
          }
                      

      • ADD
          const addTask = async task => {
            const url = '/api/todolist';
            const config = {
              method: 'POST',
              headers: {
                'Content-Type': 'application/json'
              },
              body: JSON.stringify(task)
            }
        
            try {
              const res = await fetch(url, config);
              const response = await res.json();
              console.log(response.data);
              if (res.status === 200) {
                dispatch({
                  type: ADD_TASKS,
                  payload: task
                })
              }
              else {
                alert('Error');
              }
            } catch (err) {
                alert(err);
            }
          }                
                      
      • UPDATE
          const changeServerStatus = async (status, id) => {
            const url = '/api/server/status';
            const config = {
              method: 'POST',
              headers: {
                'Content-Type': 'application/json'
              },
              body: JSON.stringify({status: status === 1 ? false : true, id})
            }
        
            try {
              const res = await fetch(url, config);
              const response = await res.json();
        
              if (res.status === 200) {
                dispatch({
                  type: CHANGE_SERVER_STATUS,
                  payload: {status, id}
                })
              } else {
                  alert('Error');
              }
            } catch(err) {
                alert(err);
            }
          }                
                      

      • DELETE
          const deleteTask = async taskId => {
            console.log(taskId);
            const url = `/api/todolist/${taskId}`;
            const config = {
              method: 'DELETE'
            }
        
            try {
              const res = await fetch(url, config);
              const response = await res.json();
              if (res.status === 200) {
                dispatch({
                  type: DELETE_TASKS,
                  payload: taskId
                })
              }
              else {
                alert('Error');
              }
            } catch (err) {
                alert(err);
            }
          }                
                      

      • FILTER and SORT
        components/servers/ServersList.js
          import React, { useContext, useEffect, useState } from 'react';
          import serversContext from '../../context/servers/serversContext';
          import ServerCard from './ServerCard';
        
          const ServersList = () => {
            const { servers, getServers } = useContext(serversContext);
            const [showRunningServersOnly, setRunningServers] = useState(false);
            const [sortByDateDesc, setSortByDateDesc] = useState(null);
        
        
            useEffect(() => {
              getServers();
              // eslint-disable-next-line
            }, []);
        
            const sortBtnHandle = () => {
              if (sortByDateDesc === null) {
                setSortByDateDesc(true);
              } else {
                  if (sortByDateDesc === true) {
                    setSortByDateDesc(false);
                  } else {
                    setSortByDateDesc(true);
                  }
              }
            }
        
            const renderServers = () => {
              let serversToShow = [...servers];
        
              if (showRunningServersOnly) {
                serversToShow = serversToShow.filter(server => server.status === 1)
              }
        
              if (sortByDateDesc !== null) {
                if (sortByDateDesc) {
                  serversToShow.sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt))       
                } else {
                  serversToShow.sort((a, b) => new Date(a.createdAt) - new Date(b.createdAt))
                }
              }
              return serversToShow.map(server => <ServerCard key={server.id} server={server} />);
            }
            return (
                <div>
                  <div className="card card-body my-2">
                    <div className="d-flex justify-content-between">
                      <div>
                        <input 
                        type="checkbox" 
                        checked={showRunningServersOnly}
                        onChange={(e) => setRunningServers(e.target.checked)}
                        /> { }
                        <label>Show Running Only</label>
                      </div>
                      <button className="btn btn-primary" onClick={sortBtnHandle}>Sort By Date</button>
                    </div>
                  </div>
                  <div className="row">
                  {renderServers()}
                  </div>
                </div>
                )
          }
          export default ServersList;
            

      • CARD COMPONENT
          import React, { useContext} from 'react';
          import serversContext from '../../context/servers/serversContext';
          
          const ServerCard = ({ server }) => {
            const { changeServerStatus } = useContext(serversContext);
            const { id, serverName, ip, companyName, status, createdAt } = server;
          
            const toggleStatus = () => {
              changeServerStatus(status, id);
            }
            return (
              <div className="col-sm-12 col-md-4 mb-3">
                <div className="card">
                  <div className="card-body">
                    <div className="d-flex justify-content-between">
                      <h4>{serverName}</h4>           
                      <div className="custom-control custom-switch">
                        <input type="checkbox" onChange={toggleStatus} className="custom-control-input" id={`customSwitch-${id}`}
                        checked={status === 1 ? 1 : 0}
                        />
                        <label className="custom-control-label" htmlFor={`customSwitch-${id}`}>
                        {status === 1 ? 'Online' : 'Offline'}</label>
                      </div>
                    </div>     
                  <p>ID: {id}</p>
                  <p>IP: {ip}</p>
                  <p>Hosting Company: {companyName}</p>
                  <p>Created at: {(new Date (createdAt)).toLocaleDateString()}</p>
                  </div>
                </div>
              </div>    
            )
          }
          
          export default ServerCard;
                        
                      

      • react-router-dom
        • App.js
            import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
            <Router>
            <Switch>
                  <Route exact path='/' component={Tasks} />
                  <Route exact path='/add-task' component={TaskForm} />
                </Switch>
            </Router>
                            
        • TaskForm.js
            import { Link } from 'react-router-dom';
            ...
            <Link to='/'>Home</Link>
                            

  11. Reminders:

  12. Tips

129,
140 - react_mysql_starter_pack - with router and form with select options - drop down list
145 - react_mysql_started_pack - with checkbox for filter and button for sort, no router