How to Build a Simple REST API in PHP for Beginners — Connect Your Project to Android or React (2026)
Your PHP management system runs beautifully on a browser. But what if a mobile app needed the same data? Or a React frontend? The answer is a REST API — and you can build a working one in PHP and MySQL in under an hour, without any framework, using only skills you already have. This is the complete beginner’s guide.
Every popular app you use — Instagram, Uber, your university’s student portal — is powered by an API. When the Instagram app on your phone shows you photos, it is not loading a PHP page. It is sending a request to an API that returns a list of photos as JSON data, which the app then displays however it wants.
This separation between “the thing that stores data” (your PHP + MySQL project) and “the thing that shows data” (a browser, a mobile app, a React component) is what REST APIs make possible. And the great news for PHP developers is that you already know everything you need to build one. A REST API in PHP is just PHP — no new language, no complex framework, just the same PHP and MySQL you already use, organised differently.
By the end of this guide you will have a working REST API built on top of a PHP management system that returns JSON, handles different types of requests, and can be connected to an Android app, a React frontend, or tested using Postman or a browser.
What Is a REST API? — The Restaurant Explanation
Before writing a single line of code, you need to understand what a REST API actually is. The best explanation is a restaurant analogy that has been used in developer education for over a decade — because it is genuinely the clearest way to understand the concept.
🍽 The Restaurant Analogy — How REST APIs Work
← Response ←
In a restaurant, you (the customer) do not walk into the kitchen and cook. You look at the menu, tell the waiter what you want, and the kitchen prepares it and brings it out. The REST API is the waiter — it takes your request to the kitchen (database), gets what you asked for, and brings it back to you in a format you can understand (JSON).
Click each HTTP method below to see what it does and when to use it:
Get customer #5
Submit new bill
Change plan price
Remove expired bill
HTTP Methods Quiz — Test Your Understanding
Quick quiz to make sure you understand HTTP methods before writing any code. 5 questions — each one based on a real scenario from PHP projects:
⚡ HTTP Methods for PHP Developers — 5 Questions
Loading…
Understanding JSON — The Language APIs Speak
REST APIs communicate in JSON (JavaScript Object Notation). JSON is just a way of representing data as text that any programming language can read. Your PHP project already uses arrays to store data — JSON is essentially the same thing, written in a universal format.
Here is the same customer data expressed as a PHP array, a MySQL row, and JSON — so you can see how they relate:
PHP PHP array → JSON → MySQL row — same data, three formats
// PHP Array (how your PHP code stores it)
$customer = [
'id' => 5,
'name' => 'John Smith',
'email' => 'john@example.com',
'plan_id' => 2,
'status' => 'active'
];
// JSON (how the API sends it to Android/React/any client)
// json_encode() converts a PHP array to JSON automatically
{
"id": 5,
"name": "John Smith",
"email": "john@example.com",
"plan_id": 2,
"status": "active"
}
// MySQL Row (how it's stored in the database)
// SELECT id, name, email, plan_id, status FROM customers WHERE id = 5;
// Returns: 5 | John Smith | john@example.com | 2 | active
// The magic: mysqli_fetch_assoc() returns a PHP array from MySQL,
// then json_encode() turns that array into JSON.
// That's all your API needs to do for a GET request.
Project Structure — What You Will Build
The 5 API endpoints
[{"id":1,"name":"John Smith","email":"john@example.com","status":"active"},{"id":2,"name":"Sarah Lee","email":"sarah@example.com","status":"active"}]
{"id":5,"name":"John Smith","email":"john@example.com","plan_id":2,"status":"active","plan_name":"100Mbps Standard"}
{"success":true,"message":"Customer created successfully","id":42}
{"success":true,"message":"Customer updated successfully"}
{"success":true,"message":"Customer deleted successfully"}
File structure
Folder Structure Add these files to your existing PHP project
your-project-folder/
├── index.php (your existing main file — unchanged)
├── config/
│ └── dbconnection.php (your existing database connection — unchanged)
├── api/ ← NEW folder
│ ├── config.php ← NEW: CORS headers and shared functions
│ └── customers.php ← NEW: the main API file for customer endpoints
api/ folder is a completely separate set of files that any client (mobile app, React, Postman) can call independently. Same database, two ways to access it.
Step 1 — Create the api/ Folder and config.php
Inside your XAMPP htdocs project folder, create a new folder called api. Inside it, create a file called config.php. This file does two important things: (1) it sets CORS headers so your API can be called from mobile apps and other websites, and (2) it includes your database connection.
PHP api/config.php — CORS headers + database connection
<?php
// ─────────────────────────────────────────────────────
// CORS Headers — allow any client to call this API
// For production: change * to your specific domain
// ─────────────────────────────────────────────────────
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, Authorization");
// Handle browser preflight OPTIONS requests
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
http_response_code(200);
exit();
}
// ─────────────────────────────────────────────────────
// Database Connection
// Update these values to match your XAMPP setup
// ─────────────────────────────────────────────────────
$host = 'localhost';
$dbname = 'isp_db'; // Your database name from phpMyAdmin
$user = 'root';
$pass = ''; // Blank by default on XAMPP
$conn = mysqli_connect($host, $user, $pass, $dbname);
if (mysqli_connect_errno()) {
http_response_code(500);
echo json_encode([
'success' => false,
'message' => 'Database connection failed: ' . mysqli_connect_error()
]);
exit();
}
?>
Access-Control-Allow-Origin: * tells the browser “this API accepts requests from any origin.” For a student project or portfolio API, this is fine. For a production application with real user data, restrict this to specific domains.
Step 2 — Build the Complete API File
Create api/customers.php. This single file handles all 5 endpoints by checking what HTTP method was used and whether an id parameter was provided. This is the most common pattern for a simple PHP REST API without a framework.
PHP api/customers.php — Complete REST API (all 5 endpoints)
<?php
// Include CORS headers and database connection
require_once 'config.php';
// Get the HTTP method and optional customer ID from the URL
// Example: GET /api/customers.php?id=5
$method = $_SERVER['REQUEST_METHOD'];
$id = isset($_GET['id']) ? (int)$_GET['id'] : null;
// Route requests to the correct handler based on method and ID
switch ($method) {
case 'GET':
$id ? getCustomer($conn, $id) : getAllCustomers($conn);
break;
case 'POST':
createCustomer($conn);
break;
case 'PUT':
$id ? updateCustomer($conn, $id) : errorResponse(400, 'Customer ID required for update');
break;
case 'DELETE':
$id ? deleteCustomer($conn, $id) : errorResponse(400, 'Customer ID required for delete');
break;
default:
errorResponse(405, 'Method not allowed');
}
// ─────────────────────────────────────────────
// GET all customers
// Endpoint: GET /api/customers.php
// ─────────────────────────────────────────────
function getAllCustomers($conn) {
$query = "SELECT c.id, c.name, c.email, c.phone, c.status,
p.plan_name, p.price
FROM customers c
LEFT JOIN internet_plans p ON c.plan_id = p.id
WHERE c.status = 'active'
ORDER BY c.name ASC";
$result = mysqli_query($conn, $query);
$customers = [];
while ($row = mysqli_fetch_assoc($result)) {
$customers[] = $row;
}
http_response_code(200);
echo json_encode($customers);
}
// ─────────────────────────────────────────────
// GET one customer by ID
// Endpoint: GET /api/customers.php?id=5
// ─────────────────────────────────────────────
function getCustomer($conn, $id) {
$stmt = mysqli_prepare($conn,
"SELECT c.*, p.plan_name, p.price
FROM customers c
LEFT JOIN internet_plans p ON c.plan_id = p.id
WHERE c.id = ?");
mysqli_stmt_bind_param($stmt, 'i', $id);
mysqli_stmt_execute($stmt);
$result = mysqli_stmt_get_result($stmt);
$customer = mysqli_fetch_assoc($result);
if ($customer) {
http_response_code(200);
echo json_encode($customer);
} else {
errorResponse(404, 'Customer not found');
}
}
// ─────────────────────────────────────────────
// POST — create a new customer
// Endpoint: POST /api/customers.php
// Body (JSON): {"name":"...","email":"...","phone":"...","plan_id":2}
// ─────────────────────────────────────────────
function createCustomer($conn) {
$body = json_decode(file_get_contents('php://input'), true);
if (!$body || !isset($body['name']) || !isset($body['email'])) {
errorResponse(400, 'Name and email are required');
return;
}
$name = mysqli_real_escape_string($conn, $body['name']);
$email = mysqli_real_escape_string($conn, $body['email']);
$phone = mysqli_real_escape_string($conn, $body['phone'] ?? '');
$plan_id = (int)($body['plan_id'] ?? 1);
$stmt = mysqli_prepare($conn,
"INSERT INTO customers (name, email, phone, plan_id, status)
VALUES (?, ?, ?, ?, 'active')");
mysqli_stmt_bind_param($stmt, 'sssi', $name, $email, $phone, $plan_id);
if (mysqli_stmt_execute($stmt)) {
http_response_code(201);
echo json_encode([
'success' => true,
'message' => 'Customer created successfully',
'id' => mysqli_insert_id($conn)
]);
} else {
errorResponse(500, 'Failed to create customer: ' . mysqli_error($conn));
}
}
// ─────────────────────────────────────────────
// PUT — update an existing customer
// Endpoint: PUT /api/customers.php?id=5
// Body (JSON): {"name":"New Name","email":"new@email.com"}
// ─────────────────────────────────────────────
function updateCustomer($conn, $id) {
$body = json_decode(file_get_contents('php://input'), true);
if (!$body) { errorResponse(400, 'No data provided'); return; }
$name = mysqli_real_escape_string($conn, $body['name'] ?? '');
$email = mysqli_real_escape_string($conn, $body['email'] ?? '');
$phone = mysqli_real_escape_string($conn, $body['phone'] ?? '');
$plan_id = (int)($body['plan_id'] ?? 1);
$stmt = mysqli_prepare($conn,
"UPDATE customers SET name=?, email=?, phone=?, plan_id=?
WHERE id=?");
mysqli_stmt_bind_param($stmt, 'sssii', $name, $email, $phone, $plan_id, $id);
if (mysqli_stmt_execute($stmt) && mysqli_stmt_affected_rows($stmt) > 0) {
http_response_code(200);
echo json_encode(['success' => true, 'message' => 'Customer updated successfully']);
} else {
errorResponse(404, 'Customer not found or no changes made');
}
}
// ─────────────────────────────────────────────
// DELETE — remove a customer
// Endpoint: DELETE /api/customers.php?id=5
// ─────────────────────────────────────────────
function deleteCustomer($conn, $id) {
$stmt = mysqli_prepare($conn, "DELETE FROM customers WHERE id = ?");
mysqli_stmt_bind_param($stmt, 'i', $id);
if (mysqli_stmt_execute($stmt) && mysqli_stmt_affected_rows($stmt) > 0) {
http_response_code(200);
echo json_encode(['success' => true, 'message' => 'Customer deleted successfully']);
} else {
errorResponse(404, 'Customer not found');
}
}
// ─────────────────────────────────────────────
// Helper: send a JSON error response
// ─────────────────────────────────────────────
function errorResponse($code, $message) {
http_response_code($code);
echo json_encode(['success' => false, 'message' => $message]);
}
Step 3 — Test Your API
You cannot test POST, PUT, and DELETE from a browser URL bar — browsers only send GET requests from the address bar. You need a tool that can send different types of HTTP requests. Here are two options:
Option A: Test in your browser (GET only)
Your GET endpoints work immediately from a browser. With XAMPP running, open these URLs to verify your API is working before testing anything else:
Open in browser — test GET endpoints immediately
# Get all active customers
http://localhost/your-project-name/api/customers.php
# Get customer with ID 1
http://localhost/your-project-name/api/customers.php?id=1
If your API is working, you will see raw JSON in your browser window. It may look messy — install the “JSON Formatter” browser extension (free on Chrome and Firefox) and it will display it as a nicely formatted, colour-coded tree.
Option B: Test with Postman (all methods)
Postman is the industry-standard tool for testing APIs. Download it free from postman.com. Here is how to test each endpoint:
| Method | URL | Body (JSON) | Expected Result |
|---|---|---|---|
| GET | localhost/project/api/customers.php | None | JSON array of all active customers |
| GET | localhost/project/api/customers.php?id=1 | None | JSON object of customer #1 |
| POST | localhost/project/api/customers.php | {“name”:”Test User”,”email”:”test@example.com”,”phone”:”07700900000″,”plan_id”:1} | {“success”:true,”id”:N} |
| PUT | localhost/project/api/customers.php?id=N | {“name”:”Updated Name”,”email”:”updated@example.com”,”phone”:”07700900001″,”plan_id”:2} | {“success”:true,”message”:”Customer updated successfully”} |
| DELETE | localhost/project/api/customers.php?id=N | None | {“success”:true,”message”:”Customer deleted successfully”} |
Setting up Postman for POST and PUT requests
- Open Postman and click New → HTTP Request
- Select POST from the method dropdown
- Enter your URL:
http://localhost/your-project/api/customers.php - Click the Body tab
- Select raw and then JSON from the dropdown
- Paste your JSON:
{"name":"Test User","email":"test@example.com","plan_id":1} - Click Send and check the response
Interactive API Tester Simulator
See what your API will return for different requests — try each endpoint below:
⚡ API Endpoint Simulator
Click "Send →" to simulate an API request
Step 4 — Call Your API from JavaScript (for React or Plain HTML)
Once your PHP API is working, calling it from JavaScript is straightforward using the fetch() function. This is how a React component or a plain HTML page would get data from your PHP API:
JavaScript fetch() — call your PHP API from any frontend
// ── GET all customers ──────────────────────────────────
fetch('http://localhost/your-project/api/customers.php')
.then(res => res.json())
.then(customers => {
customers.forEach(c => console.log(c.name, c.plan_name));
});
// ── POST — create a new customer ──────────────────────
fetch('http://localhost/your-project/api/customers.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
name: 'Sarah Jones',
email: 'sarah@example.com',
phone: '07700900123',
plan_id: 2
})
})
.then(res => res.json())
.then(data => {
if (data.success) {
console.log('Created customer with ID:', data.id);
}
});
// ── DELETE a customer ─────────────────────────────────
fetch('http://localhost/your-project/api/customers.php?id=42', {
method: 'DELETE'
})
.then(res => res.json())
.then(data => console.log(data.message));
Calling your PHP API from Android (Java)
Android Java — HTTP GET request using OkHttp (most common Android HTTP library)
// Add OkHttp to your build.gradle:
// implementation 'com.squareup.okhttp3:okhttp:4.12.0'
OkHttpClient client = new OkHttpClient();
// GET all customers from your PHP API
Request request = new Request.Builder()
.url("http://10.0.2.2/your-project/api/customers.php")
// Note: 10.0.2.2 = localhost when running in Android emulator
// For real devices on same network: use your computer's IP address
.build();
client.newCall(request).enqueue(new Callback() {
@Override public void onResponse(Call call, Response response) throws IOException {
String jsonData = response.body().string();
// Parse the JSON data here using Gson or org.json
// JSONArray customers = new JSONArray(jsonData);
}
@Override public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
});
HTTP Status Codes — Sending the Right Response
A professional API does not just return data — it returns the correct HTTP status code with every response. Status codes are three-digit numbers that tell the client whether a request succeeded or failed, and why. Your code above already uses these correctly — here is what they mean:
| Code | Meaning | When to Use |
|---|---|---|
| 200 OK | Success — here is what you asked for | Successful GET, PUT, DELETE requests |
| 201 Created | Success — a new resource was created | Successful POST requests (return the new record’s ID) |
| 400 Bad Request | The request was malformed or missing required data | Missing required fields, invalid input format |
| 404 Not Found | The requested resource does not exist | GET/PUT/DELETE with an ID that has no matching record |
| 405 Method Not Allowed | The HTTP method is not supported by this endpoint | When someone sends PATCH when your API only supports PUT |
| 500 Internal Server Error | Something went wrong on the server | Database errors, failed queries, unexpected PHP errors |
Adapting This API for Your Specific Project
The pattern you just built works for any table in any Codezips project. To add an API for a different resource — bills, doctors, inventory items — follow the same structure with two changes:
- Create a new file:
api/bills.php(or doctors.php, products.php, etc.) - Replace “customers” with your table name in all the SQL queries, and update the field names to match your actual table columns
SQL Adapt the getAllCustomers() query for the bills table
-- Original customers query:
SELECT c.id, c.name, c.email, c.status, p.plan_name, p.price
FROM customers c
LEFT JOIN internet_plans p ON c.plan_id = p.id
WHERE c.status = 'active'
-- Adapted for bills table:
SELECT b.id, b.amount, b.month, b.status,
c.name AS customer_name,
c.email AS customer_email
FROM bills b
JOIN customers c ON b.customer_id = c.id
ORDER BY b.month DESC, c.name ASC
-- Adapted for doctors table (hospital system):
SELECT d.id, d.name, d.specialization, d.phone,
w.ward_name
FROM doctors d
LEFT JOIN wards w ON d.ward_id = w.id
ORDER BY d.name ASC
Frequently Asked Questions
Do I need Laravel or another framework to build a REST API in PHP?
No — and this guide proves it. Plain PHP can build a perfectly functional REST API with no dependencies, no Composer setup, and no framework to learn. Laravel does make API development easier and more structured for larger projects, but for a student project, a portfolio API, or a first REST API you want to understand completely, plain PHP is the better learning choice. You understand every line of code you write. Once you understand the concepts from this guide, learning Laravel’s API scaffolding takes a fraction of the time because you already know what problem it is solving.
Why does my browser show a CORS error when I try to call the API from JavaScript?
The CORS error happens when your JavaScript (running on one domain or port) tries to call a PHP API on a different domain or port, and the API has not explicitly allowed it. The fix is in config.php — the lines that start with header("Access-Control-Allow-Origin: *") tell the browser “this API accepts requests from any origin.” If you added these headers and are still getting a CORS error, check: (1) the config.php file is being included at the very top of your API file before any other output, (2) there is no whitespace or HTML output before the headers are sent, and (3) your browser is not caching a previous response — try an incognito window.
Is this API secure enough for a real project?
The code in this guide uses prepared statements which protects against SQL injection — that is the most important security measure. For a student portfolio project or development learning exercise, it is appropriate. For a production API handling real user data, you should additionally implement: API key authentication (so only authorised clients can call the API), HTTPS (so data is encrypted in transit), rate limiting (to prevent abuse), and input validation more thorough than what is shown here. For learning and portfolio purposes, what is shown here demonstrates professional awareness of security through the use of prepared statements, which is the right starting point.
How do I connect this API to a React frontend?
In a React component, use the fetch() function (or the Axios library) inside a useEffect() hook to call your PHP API when the component loads. Store the returned JSON in React state using useState(). The fetch() example earlier in this guide shows the exact syntax. The key thing to get right: React runs in the browser, and your PHP API runs on localhost — make sure CORS headers are set in your config.php (they are in the code above), and use the correct localhost URL when React is running in development mode.
How do I make my API accessible from a real Android device (not just the emulator)?
In the Android emulator, 10.0.2.2 automatically routes to your development machine’s localhost — so that works out of the box. For a real Android device, the phone and your computer need to be on the same WiFi network. Then, find your computer’s local IP address (on Windows: ipconfig in Command Prompt, look for IPv4 Address, usually 192.168.x.x) and use that IP in your Android app’s URL instead of localhost. For example: http://192.168.1.15/your-project/api/customers.php. Make sure your computer’s firewall is not blocking port 80 on local network connections.
How should I add this REST API to my portfolio or CV?
Add it to your GitHub repository’s README under a “REST API” section with the endpoint list and example responses. On your CV under the project entry, add a bullet point: “Extended the system with a RESTful JSON API exposing CRUD endpoints for customer management, accessible from any HTTP client including mobile apps and React frontends.” On your portfolio website, if you have deployed the API live, add the live API URL so recruiters can actually call it and see the JSON response — this is genuinely impressive and very few student portfolios include a working live API link.
What to build next — go further with AI
You have a working REST API. The natural next step is adding an AI feature to it — for example, a POST /api/chat.php endpoint that accepts a user question and returns an AI-generated answer using the OpenAI API. That is essentially the same pattern you just learned, with one extra step: forwarding the request to OpenAI and returning the response as JSON. Read our ChatGPT chatbot tutorial to see exactly how this works.
Related Tutorials on Codezips
Last updated: April 2026. PHP code tested on PHP 8.2 with XAMPP. REST API concepts and HTTP status codes referenced from RFC 9110 and the REST API Tutorial. Android OkHttp version verified April 2026.


