Error handling is an important part of building a reliable PHP REST API. An API should detect errors, return suitable HTTP status codes, and send clear JSON error responses to the client.
API error handling means detecting problems during request processing and returning an appropriate response to the client.
Client Request
↓
PHP API
↓
Error?
┌────┴────┐
No Yes
↓ ↓
Success Error Response
↓
JSON + Status Code
Without proper error handling, an API can return confusing messages or unexpected output.
API errors should normally be accompanied by an appropriate HTTP status code.
| Status | Example Situation |
|---|---|
| 400 | Invalid request data |
| 401 | Authentication required |
| 403 | Access denied |
| 404 | Resource not found |
| 405 | Method not allowed |
| 500 | Server error |
A REST API should tell the client that it is returning JSON.
header("Content-Type: application/json");
This should be sent before outputting the JSON response.
A simple JSON error response can be created using a PHP array and json_encode().
echo json_encode([
"success" => false,
"message" => "Invalid request"
]);
http_response_code(400);
echo json_encode([
"success" => false,
"message" => "Invalid request"
]);
The client receives both the HTTP status and the JSON response.
Suppose a student API requires a student ID.
$id = $_GET['id'] ?? '';
if ($id === '') {
http_response_code(400);
echo json_encode([
"success" => false,
"message" => "Student ID is required"
]);
exit;
}
The API should validate IDs before using them in database operations.
$id = $_GET['id'] ?? '';
if (!filter_var($id, FILTER_VALIDATE_INT)) {
http_response_code(400);
echo json_encode([
"success" => false,
"message" => "Invalid student ID"
]);
exit;
}
If the requested student does not exist, the API can return HTTP 404.
if (!$student) {
http_response_code(404);
echo json_encode([
"success" => false,
"message" => "Student not found"
]);
exit;
}
A protected API can return HTTP 401 when authentication is missing or invalid.
if (!$token) {
http_response_code(401);
echo json_encode([
"success" => false,
"message" => "Authentication required"
]);
exit;
}
HTTP 403 can be used when a user is authenticated but does not have permission to perform an operation.
if ($role !== 'admin') {
http_response_code(403);
echo json_encode([
"success" => false,
"message" => "Access denied"
]);
exit;
}
An API can check the HTTP request method.
$method = $_SERVER['REQUEST_METHOD'];
if ($method !== 'GET') {
http_response_code(405);
echo json_encode([
"success" => false,
"message" => "Method not allowed"
]);
exit;
}
PHP exceptions can be handled using try and catch.
try {
// Code that may cause an exception
} catch (Exception $e) {
// Handle the exception
}
try {
$number = 10 / 0;
} catch (Exception $e) {
echo "An error occurred";
}
The catch block can handle an exception instead of allowing the application to stop unexpectedly.
PDO can be configured to throw exceptions for database errors.
$pdo->setAttribute(
PDO::ATTR_ERRMODE,
PDO::ERRMODE_EXCEPTION
);
This makes database errors easier to handle using try-catch.
try {
$stmt = $pdo->prepare(
"SELECT * FROM students"
);
$stmt->execute();
} catch (PDOException $e) {
http_response_code(500);
echo json_encode([
"success" => false,
"message" => "Database operation failed"
]);
exit;
}
Avoid sending the actual database exception message directly to the mobile application.
// Avoid this
echo $e->getMessage();
Instead, return a safe message.
echo json_encode([
"success" => false,
"message" => "Database operation failed"
]);
The actual error can be logged on the server while a safe message is returned to the client.
error_log($e->getMessage());
http_response_code(500);
echo json_encode([
"success" => false,
"message" => "Something went wrong"
]);
$name = trim($_POST['name'] ?? '');
$email = trim($_POST['email'] ?? '');
if ($name === '' || $email === '') {
http_response_code(400);
echo json_encode([
"success" => false,
"message" => "Name and email are required"
]);
exit;
}
PHP can validate an email address using filter_var().
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
http_response_code(422);
echo json_encode([
"success" => false,
"message" => "Invalid email address"
]);
exit;
}
When a REST API receives JSON, PHP can read it using php://input.
$input = file_get_contents("php://input");
$data = json_decode($input, true);
if (json_last_error() !== JSON_ERROR_NONE) {
http_response_code(400);
echo json_encode([
"success" => false,
"message" => "Invalid JSON data"
]);
exit;
}
A reusable function can make API error handling shorter and more consistent.
function sendError(
int $status,
string $message
) {
http_response_code($status);
echo json_encode([
"success" => false,
"message" => $message
]);
exit;
}
$id = $_GET['id'] ?? '';
if ($id === '') {
sendError(
400,
"Student ID is required"
);
}
The function sets the status, creates the JSON response, and stops execution.
$student = null;
if (!$student) {
sendError(
404,
"Student not found"
);
}
This keeps API code clean and easy to maintain.
try {
$stmt = $pdo->prepare(
"SELECT * FROM students"
);
$stmt->execute();
} catch (PDOException $e) {
error_log($e->getMessage());
sendError(
500,
"Unable to process request"
);
}
React Native can check the HTTP response and then read the JSON error.
fetch(url)
.then(async response => {
const data = await response.json();
if (!response.ok) {
throw new Error(data.message);
}
return data;
})
.then(data => {
console.log(data);
})
.catch(error => {
console.log(error.message);
});
React Native
↓
API Request
↓
PHP Validation
↓
Database Operation
↓
Error?
┌────┴────┐
No Yes
↓ ↓
Success Log Error
↓ ↓
JSON Status Code
Response +
JSON Error
<?php
header("Content-Type: application/json");
function sendError(
int $status,
string $message
) {
http_response_code($status);
echo json_encode([
"success" => false,
"message" => $message
]);
exit;
}
$id = $_GET['id'] ?? '';
if ($id === '') {
sendError(
400,
"Student ID is required"
);
}
if (!filter_var($id, FILTER_VALIDATE_INT)) {
sendError(
400,
"Invalid student ID"
);
}
?>
A professional PHP REST API should follow a predictable error handling process.
<?php
header("Content-Type: application/json");
function sendError(
int $status,
string $message
) {
http_response_code($status);
echo json_encode([
"success" => false,
"message" => $message
]);
exit;
}
try {
$id = $_GET['id'] ?? '';
if ($id === '') {
sendError(400, "Student ID is required");
}
if (!filter_var($id, FILTER_VALIDATE_INT)) {
sendError(400, "Invalid student ID");
}
// Database operation here
http_response_code(200);
echo json_encode([
"success" => true,
"message" => "Request successful"
]);
} catch (PDOException $e) {
error_log($e->getMessage());
sendError(
500,
"Database operation failed"
);
} catch (Exception $e) {
error_log($e->getMessage());
sendError(
500,
"Something went wrong"
);
}
?>
Question: Which PHP structure is commonly used to handle exceptions?