API Development with React.js, PHP, and MySQL – From Beginner to Advanced
Sudhanshu Gaikwad

Sudhanshu Gaikwad @sudhanshudevelopers

About: At Sudhanshu Developer, we delve into the intricate web development and software engineering world, providing insightful articles, tutorials, and resources to help you.

Location:
Hyderabad
Joined:
Jun 29, 2024

API Development with React.js, PHP, and MySQL – From Beginner to Advanced

Publish Date: May 16
5 0

Building a modern web application often involves creating a frontend (user interface) and a backend (server-side logic) that communicate through an API (Application Programming Interface). In this guide, we’ll walk you through the process of developing a full-stack application using React.js for the frontend, PHP for the backend, and MySQL as the database. This tutorial is written in simple English, covering everything from basic to advanced concepts, so anyone can understand. Let’s create a professional, SEO-friendly post for the dev.to community that ranks high on Google!


What is API Development?

An API is like a waiter in a restaurant. The frontend (customer) sends a request (order), the backend (kitchen) processes it, and the API delivers the response (food). In our case, the frontend (React.js) will send requests to the backend (PHP), which interacts with the MySQL database to store or retrieve data.

Key Terms:

  • REST API: A type of API that uses HTTP methods (GET, POST, PUT, DELETE) to perform actions.
  • Frontend: The part users see and interact with (built with React.js).
  • Backend: The server-side logic that handles requests and data (built with PHP and MySQL).

What is the Appointment Scheduling App?

The app allows users to schedule appointments (e.g., passport appointments) and manage them efficiently. It uses a REST API to connect a React.js frontend (with Styled-Components for responsive, animated UI) to a PHP backend with a MySQL database.

Key Features:

  • Book appointments with fields: Name, Email, Mobile, DOB, Appointment Date, Time (10:00 AM–5:00 PM).
  • Cancel appointments.
  • Generate a unique PNR (e.g., AB12345).
  • View all users in a table, search by PNR, and display user details in a pop-up with a download button.

App Features and Requirements

Features

  1. - Book Appointment: Users enter details and select a time slot (10:00 AM–5:00 PM).
  2. - Cancel Appointment: Cancel by PNR.
  3. - Track Appointment: Search by PNR to view details.
  4. - User Table: Display all users, search by PNR, and show details in a pop-up.

Requirements

  1. - Fields: Name, Email, Mobile, DOB, Appointment Date, Time, PNR (auto-generated, e.g., AB12345).
  2. - Time Slots: Automatically generated from 10:00 AM to 5:00 PM (30-minute intervals).
  3. - PNR: 2 letters + 5 digits (e.g., AB12345).
  4. - Responsive UI: Works on mobile and desktop with animations (e.g., fade-in, button hover).
  5. - Tools: Node.js, XAMPP, React, Styled-Components, axios.

Folder Structure

appointment-app/
├── backend/                    # PHP API and database files
   ├── db.php                  # Database connection
   ├── appointments.php        # REST API for appointments
   └── database.sql            # MySQL database setup
├── frontend/                   # React.js app
   ├── src/
      ├── components/
         ├── AppointmentForm.js  # Form for booking
         ├── UserTable.js        # Table for all users
         └── Popup.js            # Pop-up for user details
      ├── App.js                  # Main app component
      ├── App.css                 # Global styles (if needed)
      └── index.js                # React entry point
   ├── public/
      └── index.html              # HTML template
   └── package.json                # Dependencies
Enter fullscreen mode Exit fullscreen mode

Setting Up the Environment
Prerequisites

  • Node.js: For React.js (download from nodejs.org).
  • XAMPP: For PHP and MySQL (download from apachefriends.org).
  • Code Editor: VS Code or similar.
  • Browser: For testing.

Steps

  • Install XAMPP: Start Apache and MySQL.
  • Create React App: Run npx create-react-app frontend in appointment-app.
  • Install Dependencies:
  • Run npm install styled-components axios jspdfin frontend.

Backend Setup (PHP & Mysql)

1. Database Configuration

Create a Mysql database named appointment_db and a table appointments:

CREATE DATABASE appointment_db;

USE appointment_db;

CREATE TABLE appointments (
  id INT AUTO_INCREMENT PRIMARY KEY,
  name VARCHAR(100) NOT NULL,
  email VARCHAR(100) NOT NULL,
  mobile VARCHAR(15) NOT NULL,
  dob DATE NOT NULL,
  appointment_date DATE NOT NULL,
  appointment_time TIME NOT NULL,
  pnr VARCHAR(7) NOT NULL UNIQUE,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

Enter fullscreen mode Exit fullscreen mode

2. Database Connection (db.php)

Establish a connection to the Mysql database:

<?php
$host = '';
$user = '';
$password = '';
$database = 'appointment_db';

$conn = new mysqli($host, $user, $password, $database);

if ($conn->connect_error) {
  die("Connection failed: " . $conn->connect_error);
}
?>

Enter fullscreen mode Exit fullscreen mode

3. API Endpoints appointments.php

<?php
require 'db.php';

header("Access-Control-Allow-Origin: *");
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS");
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");

// Your existing code...


if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
    http_response_code(200);
    exit();
}

$method = $_SERVER['REQUEST_METHOD'];

// Generate PNR (e.g., AB12345)
function generatePNR() {
    $letters = substr(str_shuffle('ABCDEFGHIJKLMNOPQRSTUVWXYZ'), 0, 2);
    $numbers = sprintf("%05d", rand(0, 99999));
    return $letters . $numbers;
}

switch ($method) {
    // GET: Fetch all or one appointment
    case 'GET':
        if (isset($_GET['pnr'])) {
            $pnr = $_GET['pnr'];
            $stmt = $pdo->prepare("SELECT * FROM appointments WHERE pnr = ?");
            $stmt->execute([$pnr]);
            $appointment = $stmt->fetch(PDO::FETCH_ASSOC);
            echo json_encode($appointment ?: ["message" => "No appointment found"]);
        } else {
            $stmt = $pdo->query("SELECT * FROM appointments");
            $appointments = $stmt->fetchAll(PDO::FETCH_ASSOC);
            echo json_encode($appointments);
        }
        break;

    // POST: Create new appointment
    case 'POST':
        $data = json_decode(file_get_contents("php://input"));
        $pnr = generatePNR();
        $stmt = $pdo->prepare("INSERT INTO appointments (pnr, name, email, mobile, dob, appointment_date, appointment_time) VALUES (?, ?, ?, ?, ?, ?, ?)");
        $stmt->execute([$pnr, $data->name, $data->email, $data->mobile, $data->dob, $data->appointment_date, $data->appointment_time]);
        echo json_encode(["message" => "Appointment created", "pnr" => $pnr]);
        break;

    // PUT: Update appointment date/time
    case 'PUT':
        $data = json_decode(file_get_contents("php://input"));
        if (isset($data->pnr)) {
            $stmt = $pdo->prepare("UPDATE appointments SET appointment_date = ?, appointment_time = ? WHERE pnr = ?");
            $stmt->execute([$data->appointment_date, $data->appointment_time, $data->pnr]);
            echo json_encode(["message" => "Appointment updated"]);
        } else {
            echo json_encode(["message" => "Invalid request"]);
        }
        break;

    // DELETE: Cancel appointment
    case 'DELETE':
        $data = json_decode(file_get_contents("php://input"));
        $stmt = $pdo->prepare("DELETE FROM appointments WHERE pnr = ?");
        $stmt->execute([$data->pnr]);
        echo json_encode(["message" => "Appointment canceled"]);
        break;

    default:
        echo json_encode(["message" => "Unsupported request"]);
        break;
}
?>

Enter fullscreen mode Exit fullscreen mode
  • Create Appointment (create.php): Handles appointment booking.
  • Read Appointments (read.php): Fetches all appointments.
  • Update Appointment (update.php): Cancels an appointment.

Each endpoint interacts with the appointments table to perform the necessary operations.


Frontend Setup (React.js & styled-components)

1. Initialize React App

Use Create React App to set up the frontend:

npx create-react-app frontend
cd frontend
npm install styled-components axios react-modal

Enter fullscreen mode Exit fullscreen mode

1. App Component(App.js):

import React, { useState } from "react";
import { BrowserRouter as Router, Routes, Route, Link } from "react-router-dom";
import styled from "styled-components";
import AppointmentForm from "./components/AppointmentForm";
import UserTable from "./components/UserTable";

const Container = styled.div`
  max-width: 1200px;
  margin: auto;
  padding: 20px;
  font-family: Arial, sans-serif;

  @media (max-width: 768px) {
    padding: 10px;
  }
`;

const Nav = styled.nav`
  display: flex;
  gap: 20px;
  margin-bottom: 20px;
  flex-wrap: wrap;

  a {
    text-decoration: none;
    color: #333;
    padding: 10px 15px;
    background-color: #e0e0e0;
    border-radius: 5px;
    transition: background-color 0.3s ease;

    &:hover {
      background-color: #ccc;
    }
  }

  @media (max-width: 600px) {
    gap: 10px;
    a {
      padding: 8px 12px;
      font-size: 14px;
    }
  }
`;

const Heading = styled.h1`
  text-align: center;
  margin-bottom: 30px;
  color: #007bff;

  @media (max-width: 600px) {
    font-size: 24px;
  }
`;

const ArticleLink = styled.div`
  text-align: center;
  margin-bottom: 20px;

  a {
    font-size: 18px;
    text-decoration: none;
    color: #007bff;
    border-bottom: 2px solid #007bff;
    transition: color 0.3s ease;

    &:hover {
      color: #0056b3;
    }
  }
`;

const InfoContainer = styled.div`
  text-align: center;
  margin-bottom: 40px;
  font-size: 16px;
  color: #555;

  ul {
    text-align: left;
    margin: 0 auto;
    display: inline-block;
  }

  li {
    margin: 10px 0;
  }
`;

const App = () => {
  const [selectedAppointment, setSelectedAppointment] = useState(null);

  return (
    <Router>
      <Container>
        <ArticleLink>
          <a href="https://dev.to" target="_blank" rel="noopener noreferrer">
            Read the Full Article on API Development with React.js, PHP, and
            MySQL
          </a>
        </ArticleLink>

        <Nav>
          <Link to="/appointment">Home</Link>
          <Link to="/UserTable">User Table</Link>
        </Nav>
        <Routes>
          <Route path="/" element={<AppointmentForm />} />
          <Route path="/appointment" element={<AppointmentForm />} />
          <Route path="/UserTable" element={<UserTable />} />
        </Routes>
      </Container>
    </Router>
  );
};

export default App;

Enter fullscreen mode Exit fullscreen mode

2. Appointment Form (AppointmentForm.jsx)

Create a form for users to book appointments:

import React, { useState } from "react";
import styled from "styled-components";
import axios from "axios";

const Form = styled.form`
  display: flex;
  flex-direction: column;
  gap: 20px;
  max-width: 600px;
  margin: 20px auto;
  padding: 25px;
  border: 1px solid #ddd;
  border-radius: 10px;
  background: #f9f9f9;

  @media (max-width: 768px) {
    padding: 15px;
    gap: 15px;
  }

  @media (max-width: 480px) {
    padding: 10px;
  }
`;

const Field = styled.div`
  display: flex;
  flex-direction: column;
`;

const Label = styled.label`
  font-size: 15px;
  margin-bottom: 5px;
  color: #333;
`;

const Input = styled.input`
  padding: 10px;
  font-size: 15px;
  border: 1px solid #ccc;
  border-radius: 5px;

  &:focus {
    border-color: #007bff;
    outline: none;
  }
`;

const Select = styled.select`
  padding: 10px;
  font-size: 15px;
  border: 1px solid #ccc;
  border-radius: 5px;
`;

const Button = styled.button`
  padding: 10px;
  background: #ffbf00;
  color: #000000;
  border: none;
  border-radius: 5px;
  font-size: 15px;
  cursor: pointer;
  transition: background 0.3s, transform 0.2s;

  &:hover {
    background: #ecb40b;
    transform: scale(1.05);
  }
`;

const Message = styled.p`
  color: ${(props) => (props.error ? "red" : "green")};
  text-align: center;
`;

const AppointmentForm = () => {
  const [formData, setFormData] = useState({
    name: "",
    email: "",
    mobile: "",
    dob: "",
    appointment_date: "",
    appointment_time: "",
  });
  const [pnr, setPnr] = useState("");
  const [message, setMessage] = useState("");
  const [isError, setIsError] = useState(false);

  const timeSlots = [];
  for (let hour = 10; hour <= 17; hour++) {
    for (let min = 0; min < 60; min += 30) {
      if (hour === 17 && min > 0) break;
      const time = `${hour.toString().padStart(2, "0")}:${min
        .toString()
        .padStart(2, "0")}`;
      timeSlots.push(time);
    }
  }

  const handleChange = (e) => {
    setFormData({ ...formData, [e.target.name]: e.target.value });
  };

  const handleSubmit = async (e) => {
    e.preventDefault();
    try {
      const response = await axios.post(
        "http://localhost/Appointment_scheduling_Application_DB/appointments.php",
        formData
      );
      setPnr(response.data.pnr);
      setMessage("Appointment booked! PNR: " + response.data.pnr);
      setIsError(false);
      setFormData({
        name: "",
        email: "",
        mobile: "",
        dob: "",
        appointment_date: "",
        appointment_time: "",
      });
    } catch (error) {
      setMessage("Error booking appointment");
      setIsError(true);
    }
  };

  const handleCancel = async () => {
    if (!pnr) {
      setMessage("Enter PNR to cancel");
      setIsError(true);
      return;
    }
    try {
      await axios.delete("http://localhost/appointments.php", {
        data: { pnr },
      });
      setMessage("Appointment canceled");
      setIsError(false);
      setPnr("");
    } catch (error) {
      setMessage("Error canceling appointment");
      setIsError(true);
    }
  };

  return (
    <Form onSubmit={handleSubmit}>
      <Field>
        <Label htmlFor="name">Name</Label>
        <Input
          id="name"
          type="text"
          name="name"
          value={formData.name}
          onChange={handleChange}
          required
        />
      </Field>

      <Field>
        <Label htmlFor="email">Email</Label>
        <Input
          id="email"
          type="email"
          name="email"
          value={formData.email}
          onChange={handleChange}
          required
        />
      </Field>

      <Field>
        <Label htmlFor="mobile">Mobile</Label>
        <Input
          id="mobile"
          type="tel"
          name="mobile"
          value={formData.mobile}
          onChange={handleChange}
          required
        />
      </Field>

      <Field>
        <Label htmlFor="dob">Date of Birth</Label>
        <Input
          id="dob"
          type="date"
          name="dob"
          value={formData.dob}
          onChange={handleChange}
          required
        />
      </Field>

      <Field>
        <Label htmlFor="appointment_date">Appointment Date</Label>
        <Input
          id="appointment_date"
          type="date"
          name="appointment_date"
          value={formData.appointment_date}
          onChange={handleChange}
          required
        />
      </Field>

      <Field>
        <Label htmlFor="appointment_time">Appointment Time</Label>
        <Select
          id="appointment_time"
          name="appointment_time"
          value={formData.appointment_time}
          onChange={handleChange}
          required
        >
          <option value="">Select Time</option>
          {timeSlots.map((time) => (
            <option key={time} value={time}>
              {time}
            </option>
          ))}
        </Select>
      </Field>

      <Button type="submit">Book Appointment</Button>

      {message && <Message error={isError}>{message}</Message>}
    </Form>
  );
};

export default AppointmentForm;


Enter fullscreen mode Exit fullscreen mode

3. User Table (UserTable.jsx)

Display all appointments in a searchable table:

import React, { useState, useEffect } from "react";
import styled from "styled-components";
import axios from "axios";

const TableContainer = styled.div`
  margin: 20px 0;
  overflow-x: auto;
`;

const Table = styled.table`
  width: 100%;
  border-collapse: collapse;
  background: #fff;
  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);

  th,
  td {
    padding: 12px;
    text-align: left;
    border-bottom: 1px solid #ddd;
  }

  th {
    background: #ffbf00;
    color: #000000;
  }

  tr:hover {
    background: #f1f1f1;
    cursor: pointer;
  }

  @media (max-width: 768px) {
    th,
    td {
      padding: 8px;
      font-size: 14px;
    }
  }
`;

const SearchContainer = styled.div`
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-bottom: 20px;
`;

const SearchInput = styled.input`
  padding: 10px;
  width: 100%;
  max-width: 250px;
  border: 1px solid #ccc;
  border-radius: 4px;
`;

const CancelButton = styled.button`
  background-color: #ff4d4d;
  color: white;
  border: none;
  padding: 8px 16px;
  cursor: pointer;
  border-radius: 4px;

  &:hover {
    background-color: #ff1a1a;
  }
`;

const UserTable = ({ onSelectAppointment }) => {
  const [appointments, setAppointments] = useState([]);
  const [searchFields, setSearchFields] = useState({
    pnr: "",
    name: "",
    mobile: "",
  });

  useEffect(() => {
    const fetchAppointments = async () => {
      try {
        const queryParams = new URLSearchParams();
        if (searchFields.pnr) queryParams.append("pnr", searchFields.pnr);
        if (searchFields.name) queryParams.append("name", searchFields.name);
        if (searchFields.mobile)
          queryParams.append("mobile", searchFields.mobile);

        const url = `http://localhost/Appointment_scheduling_Application_DB/appointments.php${
          queryParams.toString() ? `?${queryParams.toString()}` : ""
        }`;

        const response = await axios.get(url);
        setAppointments(
          Array.isArray(response.data) ? response.data : [response.data]
        );
      } catch (error) {
        console.error("Error fetching appointments:", error);
      }
    };

    fetchAppointments();
  }, [searchFields]);

  const handleInputChange = (e) => {
    const { name, value } = e.target;
    setSearchFields((prev) => ({ ...prev, [name]: value }));
  };

  const handleCancelAppointment = async (pnr) => {
    const confirmDelete = window.confirm(
      "Are you sure you want to delete this appointment?"
    );
    if (confirmDelete) {
      try {
        await axios.delete(
          "http://localhost/Appointment_scheduling_Application_DB/appointments.php",
          {
            data: { pnr },
          }
        );

        // Remove the canceled appointment from the list
        setAppointments((prevAppointments) =>
          prevAppointments.filter((appointment) => appointment.pnr !== pnr)
        );
      } catch (error) {
        console.error("Error canceling appointment:", error);
      }
    }
  };

  return (
    <TableContainer>
      <SearchContainer>
        <SearchInput
          name="pnr"
          type="text"
          value={searchFields.pnr}
          onChange={handleInputChange}
          placeholder="Search by PNR"
        />
      </SearchContainer>
      <Table>
        <thead>
          <tr>
            <th>PNR</th>
            <th>Name</th>
            <th>Email</th>
            <th>Mobile</th>
            <th>Appointment Date</th>
            <th>Appointment Time</th>
            <th>Action</th>
          </tr>
        </thead>
        <tbody>
          {appointments.map((appointment) => (
            <tr key={appointment.pnr}>
              <td>{appointment.pnr}</td>
              <td>{appointment.name}</td>
              <td>{appointment.email}</td>
              <td>{appointment.mobile}</td>
              <td>{appointment.appointment_date}</td>
              <td>{appointment.appointment_time}</td>
              <td>
                <CancelButton
                  onClick={() => handleCancelAppointment(appointment.pnr)}
                >
                  Cancel Appointment
                </CancelButton>
              </td>
            </tr>
          ))}
        </tbody>
      </Table>
    </TableContainer>
  );
};

export default UserTable;

Enter fullscreen mode Exit fullscreen mode

4. Main File( index.js ):

The App.js component is included here.

Image description

PNR Generation Logic

Generate a unique PNR with 2 alphabets followed by 5 digits:

function generatePNR() {
  $letters = substr(str_shuffle("ABCDEFGHIJKLMNOPQRSTUVWXYZ"), 0, 2);
  $numbers = substr(str_shuffle("0123456789"), 0, 5);
  return $letters . $numbers;
}

Enter fullscreen mode Exit fullscreen mode

Your Appointment Scheduling Web App project looks like this.

Image description

Image description


Conclusion

By following this tutorial, you've built a responsive appointment scheduling application using React.js, styled-components, PHP, and MySQL. This project demonstrates full-stack development, integrating frontend responsiveness and animations with backend data management.

Comments 0 total

    Add comment