Creating a Database-Driven API with PHP and MySQL
InThis thistutorial tutorial,creates youa willread-only createJSON anendpoint using PHP, PDO and fictional database records.
An API thatshould readsreturn only the data fromneeded afor MySQLits stated purpose. Do not expose password hashes, session data, personal information or unnecessary database and returns it as JSON.fields.
The System Architecture
The completed system will work like this:
security_events table
↓
api.php
↓
JSON
↓
JavaScript
Instead of reading a JSON file, the API will generate JSON directly from the database.
Create the API Fileendpoint
Create:
api_events.api/events.php
Add the database connection::
<?php
declare(strict_types=1);
require __DIR__ . '/../includes/database.php';
header('Content-Type: application/json; charset=utf-8');
header('Cache-Control: no-store');
try {
$connstatement = new$pdo->query(
mysqli('SELECT
"localhost",event_id,
"root",event_timestamp,
"",device_id,
"project_db"room,
event_type,
severity,
access_result
FROM security_events
ORDER BY event_timestamp DESC
LIMIT 100'
);
if$events ($conn->connect_error) {
die("Connection failed: " .= $conn-statement->connect_error)fetchAll();
echo json_encode(
[
'ok' => true,
'count' => count($events),
'events' => $events
],
JSON_THROW_ON_ERROR | JSON_UNESCAPED_SLASHES
);
} ?catch (Throwable $error) {
http_response_code(500);
echo json_encode([
'ok' => false,
'error'
Set=> the'The Contentfictional Type
event Add:
header(is "Content-Type:unavailable.'
application/json"
]);
}
The API will now return JSON instead of HTML.endpoint:
Query
uses the Add:
$queryconnection;
This retrieves all records fromonly the table.
fields;
Create
limits Add:
$eventsreturns =a [];predictable object; Thisand
array
details.
Loop Through the Results
Add:
while (
$row =
$result->fetch_assoc()
) {
$events[] = $row;
}
This converts each database record into an array item.
Convert the Array to JSON
Add:
echo json_encode(
$events,
JSON_PRETTY_PRINT
);
The completed API should now return JSON.
Test the APIresponse
Open:Open the endpoint through XAMPP:
http://localhost/api-demo/api_events.project/api/events.php
YouA successful response should see:have this shape:
{
"ok": true,
"count": 2,
"events": [
{
"event_id": 14,
"1"event_timestamp": "2026-06-14 20:57:06",
"device_id": "CAM-01",
"room": "Science Lab",
"event_type": "motion_detected"failed_login",
"severity":"high"
},
{ "event_id":"2"medium",
"device_id"access_result":"CAM-01", "event_type":"remote_access_attempt",
"severity":"critical"denied"
}
]
}
The exactvalues recordsare fictional. Your live response will dependreflect onthe yourcurrent database.
CompleteImportant API Fileboundary
<?php
$conn = new mysqli(
"localhost",
"root",
"",
"project_db"
);
if ($conn->connect_error) {
die("Connection failed: " .
$conn->connect_error);
}
header(
"Content-Type: application/json"
);
$query = "
SELECT *
FROM security_events
";
$result =
$conn->query($query);
$events = [];
while (
$row =
$result->fetch_assoc()
) {
$events[] = $row;
}
echo json_encode(
$events,
JSON_PRETTY_PRINT
);
?>
Create a Webpage to Read the API
Create:
events.html
Add:
<!DOCTYPE html>
<html>
<head>
<title>Security Events</title>
</head>
<body>
<h1>Security Events</h1>
<div id="output"></div>
<script>
fetch("api_events.php")
.then(response => response.json())
.then(data => {
let html = `
<table border="1">
<tr>
<th>Device</th>
<th>Event Type</th>
<th>Severity</th>
</tr>
`;
data.forEach(event => {
html += `
<tr>
<td>${event.device_id}</td>
<td>${event.event_type}</td>
<td>${event.severity}</td>
</tr>
`;
});
html += "</table>";
document.getElementById(
"output"
).innerHTML = html;
});
</script>
</body>
</html>
Test the Complete System
Open:
http://localhost/api-demo/events.html
The page should display the records stored in MySQL.
Why This Is Useful
Previously:
JSON File
↓
JavaScript
Now:
MySQL Database
↓
PHP API
↓
JavaScript
This means:example is a public, read-only classroom endpoint. A production API may also require authentication, authorisation, pagination, rate limiting, audit logging and a documented privacy purpose.
Check
NewTherecordsendpointcanreturnsbevalidadded to the databaseJSON.- The
APIresponseupdatesusesautomaticallyan appropriate HTTP status when processing fails.
This is the foundation of many modern web applications.