# Working with APIs

Create PHP JSON APIs and retrieve database records through focused endpoints.

# Creating a Simple JSON API with PHP

In this tutorial, you will create a simple API using PHP.

The API will return JSON data when visited in a browser.

This is the same type of data format used by many real-world APIs.

---

## Project Structure

Create a folder called:

```text
api-demo
```

Inside the folder place:

```text
api-demo
├── api.php
└── smart_security_data.json
```

---

## Create the API File

Create:

```text
api.php
```

Add:

```php
<?php

header(
    "Content-Type: application/json"
);

echo file_get_contents(
    "smart_security_data.json"
);

?>
```

Save the file.

---

## Understanding the Code

This line tells the browser that JSON data is being returned:

```php
header(
    "Content-Type: application/json"
);
```

This line reads the JSON file:

```php
file_get_contents(
    "smart_security_data.json"
);
```

This line sends the JSON data to the browser:

```php
echo file_get_contents(
    "smart_security_data.json"
);
```

---

## Test the API

Open:

```text
http://localhost/api-demo/api.php
```

You should see:

```json
{
  "homeId": "GC-HOME-014",
  "location": "Gold Coast"
}
```

along with the rest of the JSON data.

## Test the API in a New Browser Tab

Notice that the URL now behaves like a data source.

Instead of displaying a webpage, it returns structured JSON data.

This is the same concept used by many modern APIs.

Example:

```text
https://example.com/api/users
https://example.com/api/products
https://example.com/api/weather
```

---


## Complete API File

```php
<?php

header(
    "Content-Type: application/json"
);

echo file_get_contents(
    "smart_security_data.json"
);

?>
```

You have successfully created a JSON API using PHP.

# Reading API Data with JavaScript

The API returns JSON data when visited in a browser.

In this tutorial, you will use JavaScript to read data from the API and display it on a webpage.

---

## Project Structure

Your project should contain:

```text
api-demo
├── api.php
├── index.html
└── smart_security_data.json
```

---

## Create the HTML Page

Create:

```text
index.html
```

Add:

```html
<!DOCTYPE html>
<html>
<head>
    <title>API Demo</title>
</head>
<body>

<h1>API Dashboard</h1>

<div id="output"></div>

<script>

</script>

</body>
</html>
```

Save the file.

---

## Read the API

Inside the `<script>` tags add:

```javascript
fetch("api.php")
    .then(response => response.json())
    .then(data => {

        console.log(data);

    });
```

Save the file.

---

## Test the API Request

Open:

```text
http://localhost/api-demo/
```

Press:

```text
F12
```

Open the **Console** tab.

You should see the JSON object displayed.

## Display the Home Information

Replace:

```javascript
console.log(data);
```

with:

```javascript
document.getElementById(
    "output"
).innerHTML = `

    <p>
        <strong>Home ID:</strong>
        ${data.homeId}
    </p>

    <p>
        <strong>Location:</strong>
        ${data.location}
    </p>

`;
```

Refresh the page.

You should see:

```text
Home ID: GC-HOME-014

Location: Gold Coast
```

## Display Device Information

Replace the existing code with:

```javascript
let html = `

    <h2>Devices</h2>

    <table border="1">

        <tr>
            <th>Device ID</th>
            <th>Device Type</th>
            <th>Room</th>
        </tr>

`;

data.devices.forEach(device => {

    html += `

        <tr>
            <td>${device.deviceId}</td>
            <td>${device.deviceType}</td>
            <td>${device.room}</td>
        </tr>

    `;

});

html += "</table>";

document.getElementById(
    "output"
).innerHTML = html;
```

Refresh the page.

You should now see a table of devices.

## Why Use an API?

Previously, the data was loaded directly from:

```javascript
fetch("smart_security_data.json")
```

Now the data is loaded from:

```javascript
fetch("api.php")
```

This means the data can be:

* Generated dynamically
* Stored in a database
* Updated automatically
* Filtered before being sent

The webpage does not need to know where the data comes from. It only needs the JSON returned by the API.

---

## Complete Page

```html
<!DOCTYPE html>
<html>
<head>
    <title>API Demo</title>
</head>
<body>

<h1>API Dashboard</h1>

<div id="output"></div>

<script>

fetch("api.php")
    .then(response => response.json())
    .then(data => {

        let html = `

            <h2>Devices</h2>

            <table border="1">

                <tr>
                    <th>Device ID</th>
                    <th>Device Type</th>
                    <th>Room</th>
                </tr>

        `;

        data.devices.forEach(device => {

            html += `

                <tr>
                    <td>${device.deviceId}</td>
                    <td>${device.deviceType}</td>
                    <td>${device.room}</td>
                </tr>

            `;

        });

        html += "</table>";

        document.getElementById(
            "output"
        ).innerHTML = html;

    });

</script>

</body>
</html>
```

You have successfully consumed data from an API using JavaScript.

# Creating a Database-Driven API with PHP and MySQL

This tutorial creates a read-only JSON endpoint using PHP, PDO and fictional database records.

An API should return only the data needed for its stated purpose. Do not expose password hashes, session data, personal information or unnecessary database fields.

## Create the endpoint

Create `api/events.php`:

```php
<?php
declare(strict_types=1);

require __DIR__ . '/../includes/database.php';

header('Content-Type: application/json; charset=utf-8');
header('Cache-Control: no-store');

try {
    $statement = $pdo->query(
        'SELECT
            event_id,
            event_timestamp,
            device_id,
            room,
            event_type,
            severity,
            access_result
         FROM security_events
         ORDER BY event_timestamp DESC
         LIMIT 100'
    );

    $events = $statement->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' => 'The fictional event data is unavailable.'
    ]);
}
```

The endpoint:

- uses the shared PDO connection;
- selects only the required fields;
- limits the response size;
- returns a predictable object; and
- avoids exposing database error details.

## Test the response

Open the endpoint through XAMPP:

```text
http://localhost/project/api/events.php
```

A successful response should have this shape:

```json
{
  "ok": true,
  "count": 2,
  "events": [
    {
      "event_id": 14,
      "event_timestamp": "2026-06-14 20:57:06",
      "device_id": "CAM-01",
      "room": "Science Lab",
      "event_type": "failed_login",
      "severity": "medium",
      "access_result": "denied"
    }
  ]
}
```

The values are fictional. Your live response will reflect the current database.

## Important boundary

This 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

- The endpoint returns valid JSON.
- The response uses an appropriate HTTP status when processing fails.
- No credentials, password hashes or unnecessary fields are exposed.
- The response is limited to a reasonable number of records.
- All records are fictional.

# Creating API Endpoints for Specific Data

Focused endpoints return the smallest useful result for a particular user need. This tutorial adds filtered and summary endpoints using PDO and fictional records.

## Endpoint for one severity

Create `api/events-by-severity.php`:

```php
<?php
declare(strict_types=1);

require __DIR__ . '/../includes/database.php';

header('Content-Type: application/json; charset=utf-8');
header('Cache-Control: no-store');

$allowed = ['low', 'medium', 'high', 'critical'];
$severity = strtolower(trim($_GET['severity'] ?? ''));

if (!in_array($severity, $allowed, true)) {
    http_response_code(400);
    echo json_encode([
        'ok' => false,
        'error' => 'Choose low, medium, high or critical.'
    ]);
    exit;
}

$statement = $pdo->prepare(
    'SELECT
        event_id,
        event_timestamp,
        device_id,
        room,
        event_type,
        severity
     FROM security_events
     WHERE severity = :severity
     ORDER BY event_timestamp DESC
     LIMIT 100'
);

$statement->execute(['severity' => $severity]);
$events = $statement->fetchAll();

echo json_encode([
    'ok' => true,
    'filter' => ['severity' => $severity],
    'count' => count($events),
    'events' => $events
]);
```

Test it with a fictional category:

```text
http://localhost/project/api/events-by-severity.php?severity=high
```

The allow-list rejects unexpected values and the prepared statement keeps the supplied value separate from the SQL.

## Endpoint for summary totals

Create `api/event-summary.php`:

```php
<?php
declare(strict_types=1);

require __DIR__ . '/../includes/database.php';

header('Content-Type: application/json; charset=utf-8');
header('Cache-Control: no-store');

$statement = $pdo->query(
    'SELECT severity, COUNT(*) AS total
     FROM security_events
     GROUP BY severity
     ORDER BY severity'
);

$rows = $statement->fetchAll();

$summary = [];

foreach ($rows as $row) {
    $summary[] = [
        'severity' => $row['severity'],
        'total' => (int) $row['total']
    ];
}

echo json_encode([
    'ok' => true,
    'summary' => $summary
]);
```

This endpoint returns processed information rather than every event record. It can support an accessible table or chart.

## Choose endpoints from user needs

Useful endpoints might answer:

- Which fictional events require urgent review?
- How many events occurred in each category?
- Which events belong to a selected device?
- How has the event count changed over time?

Avoid creating many endpoints that expose the same unnecessary fields.

## Check

- Invalid filters return HTTP 400 and a clear JSON error.
- User-supplied values are validated and passed through prepared statements.
- Totals are cast to numbers before encoding.
- Responses contain only the required fields.
- No personal information or credentials are exposed.
- All demonstration records are fictional.