This mini project adds a small JSON API alongside your CRUD app – or as a separate endpoint group. Same auth ideas, different response format. Useful for mobile clients, JavaScript front ends, or webhooks.
API routes
GET /api/notes list current user's notes (JSON)
GET /api/notes/{id} single note
POST /api/notes create (JSON body)
PUT /api/notes/{id} update
DELETE /api/notes/{id} delete
JSON responses
function jsonResponse(mixed $data, int $status = 200): void
{
http_response_code($status);
header('Content-Type: application/json; charset=utf-8');
echo json_encode($data, JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE);
exit;
}
// Success:
jsonResponse(['notes' => $notes]);
// Error:
jsonResponse(['error' => 'Title is required'], 422);
Reading JSON input
$raw = file_get_contents('php://input');
$input = json_decode($raw, true, 512, JSON_THROW_ON_ERROR);
$title = trim($input['title'] ?? '');
Auth for APIs
Browser forms use session cookies. APIs often use a token in the Authorization header. For learning, session cookies on same-origin fetch calls are fine. For a token approach:
$header = $_SERVER['HTTP_AUTHORIZATION'] ?? '';
if (preg_match('/Bearers+(S+)/', $header, $m)) {
$token = $m[1];
// Look up user by api_token hash in database
}
Consistent error shape
jsonResponse([
'error' => 'Validation failed',
'fields' => ['title' => 'Required'],
], 422);
CORS (if needed)
If a JavaScript app on another domain calls your API, send CORS headers deliberately – do not use * with credentials. For same-site apps you may not need CORS at all.
Testing with curl
curl -X POST http://localhost:8080/api/notes
-H 'Content-Type: application/json'
-H 'Cookie: PHPSESSID=your_session_id'
-d '{"title":"Test","body":"Hello"}'
You now have HTML pages for humans and JSON for machines, sharing the same repositories and auth rules. That split is how most modern PHP apps are structured – even before you reach for a full framework.

