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
- - Book Appointment: Users enter details and select a time slot (10:00 AM–5:00 PM).
- - Cancel Appointment: Cancel by PNR.
- - Track Appointment: Search by PNR to view details.
- - User Table: Display all users, search by PNR, and show details in a pop-up.
Requirements
- - Fields: Name, Email, Mobile, DOB, Appointment Date, Time, PNR (auto-generated, e.g., AB12345).
- - Time Slots: Automatically generated from 10:00 AM to 5:00 PM (30-minute intervals).
- - PNR: 2 letters + 5 digits (e.g., AB12345).
- - Responsive UI: Works on mobile and desktop with animations (e.g., fade-in, button hover).
- - 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
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
jspdf
in 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 );
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); } ?>
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; } ?>
- 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
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;
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;
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;
4. Main File( index.js
):
The App.js
component is included here.
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; }
Your Appointment Scheduling Web App project looks like this.
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.
Top comments (0)