Middleware is code that runs between the incoming API request and the final API endpoint. It can perform common tasks such as authentication, authorization, logging, validation, CORS handling, and request processing.
Middleware is a layer of code that processes a request before it reaches the main API endpoint.
Mobile App
↓
API Request
↓
Middleware
↓
API Endpoint
↓
Database
↓
JSON Response
Middleware can also process or modify the response before it is returned to the client.
Middleware is useful when the same operation is required by many API endpoints.
Request
↓
Authentication Middleware
↓
Authorization Middleware
↓
Validation Middleware
↓
API Controller
↓
Database
↓
Response
Each middleware can allow the request to continue or stop it.
A middleware function can check a condition before allowing the request to reach the API endpoint.
if ($userIsAuthenticated) {
// Continue to API
} else {
// Stop request
}
For example, authentication middleware can reject a request when a valid JWT token is missing.
Authentication middleware checks whether the client is properly authenticated.
Authorization: Bearer JWT_TOKEN
The middleware can extract the token, verify it, and identify the user.
A JWT middleware can verify the token before allowing access to protected APIs.
Request
↓
Read Authorization Header
↓
Extract JWT
↓
Verify JWT
↓
Valid?
↙ ↘
Yes No
↓ ↓
API 401
A simple PHP middleware function can check for an Authorization header.
function authMiddleware()
{
$headers = getallheaders();
if (!isset($headers['Authorization'])) {
http_response_code(401);
echo json_encode([
"success" => false,
"message" => "Authorization required"
]);
exit;
}
}
Middleware can be called before the main API logic.
require_once 'middleware.php';
authMiddleware();
// Protected API logic starts here
echo json_encode([
"success" => true,
"message" => "Protected data"
]);
The Authorization header normally contains the Bearer token.
$headers = getallheaders();
$authHeader = $headers['Authorization'] ?? '';
if (preg_match('/Bearer\s+(.*)$/i', $authHeader, $matches)) {
$token = $matches[1];
}
The extracted token can then be passed to the JWT verification logic.
JWT verification should happen before protected API operations.
function authMiddleware()
{
$headers = getallheaders();
$authHeader = $headers['Authorization'] ?? '';
if (!preg_match(
'/Bearer\s+(.*)$/i',
$authHeader,
$matches
)) {
http_response_code(401);
echo json_encode([
"success" => false,
"message" => "Token required"
]);
exit;
}
$token = $matches[1];
// Verify JWT here
return $token;
}
Authentication answers:
"Who are you?"
Authorization answers:
"Are you allowed to perform this operation?"
if ($userRole !== 'admin') {
http_response_code(403);
echo json_encode([
"success" => false,
"message" => "Access forbidden"
]);
exit;
}
Middleware can check the user's role before allowing access.
function adminMiddleware($user)
{
if ($user['role'] !== 'admin') {
http_response_code(403);
echo json_encode([
"success" => false,
"message" => "Admin access required"
]);
exit;
}
}
Logging middleware can record information about API requests.
error_log(
$_SERVER['REQUEST_METHOD'] .
' ' .
$_SERVER['REQUEST_URI']
);
Logs can help developers understand API activity and diagnose problems.
Never log sensitive information unnecessarily.
Log only information that is useful for debugging, monitoring, and security.
Middleware can also be used to add CORS headers to API responses.
header("Content-Type: application/json");
header("Access-Control-Allow-Origin: https://example.com");
header("Access-Control-Allow-Methods: GET, POST, PUT, DELETE");
header("Access-Control-Allow-Headers: Content-Type, Authorization");
CORS configuration should be appropriate for the application's environment.
Browser clients can send an OPTIONS preflight request before certain cross-origin requests.
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
http_response_code(204);
exit;
}
This logic can be placed in shared middleware when required.
Middleware can perform common request validation before an endpoint runs.
function requireJsonRequest()
{
$contentType = $_SERVER['CONTENT_TYPE'] ?? '';
if (stripos($contentType, 'application/json') === false) {
http_response_code(415);
echo json_encode([
"success" => false,
"message" => "JSON request required"
]);
exit;
}
}
Middleware can check whether an endpoint supports the requested HTTP method.
$allowedMethods = ['GET', 'POST'];
if (!in_array(
$_SERVER['REQUEST_METHOD'],
$allowedMethods,
true
)) {
http_response_code(405);
echo json_encode([
"success" => false,
"message" => "Method not allowed"
]);
exit;
}
Rate limiting middleware can restrict how many requests a client can make within a period.
Client
↓
Rate Limit Middleware
↓
Request Count
↓
Limit Reached?
↙ ↘
No Yes
↓ ↓
API 429
A production application should use a reliable shared storage mechanism for rate-limit counters.
A unique request ID can help connect server logs with a particular API request.
$requestId = bin2hex(random_bytes(8));
header(
"X-Request-ID: " . $requestId
);
error_log(
"Request ID: " . $requestId
);
The request ID can also be returned in the API response when useful.
An API can use multiple middleware layers.
Request
↓
CORS Middleware
↓
Request ID Middleware
↓
Authentication Middleware
↓
Authorization Middleware
↓
Validation Middleware
↓
API Endpoint
This keeps different responsibilities separated.
A PHP API can keep middleware in a separate directory.
api/
│
├── middleware/
│ ├── auth.php
│ ├── cors.php
│ ├── logging.php
│ └── validation.php
│
├── students.php
├── users.php
└── profile.php
This structure helps organize reusable API components.
A reusable authentication function can be included by multiple endpoints.
// middleware/auth.php
function requireAuth()
{
$headers = getallheaders();
$authHeader = $headers['Authorization'] ?? '';
if (!$authHeader) {
http_response_code(401);
echo json_encode([
"success" => false,
"message" => "Authentication required"
]);
exit;
}
// JWT verification logic
return true;
}
require_once 'middleware/auth.php';
requireAuth();
$stmt = $pdo->query(
"SELECT id, name, email
FROM students"
);
$students = $stmt->fetchAll(PDO::FETCH_ASSOC);
echo json_encode([
"success" => true,
"message" => "Students retrieved successfully",
"data" => $students
]);
The endpoint can focus on its main student-related operation.
React Native sends a normal HTTP request. The server executes middleware before returning the API response.
const response = await fetch(API_URL, {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`,
"Content-Type": "application/json"
}
});
const result = await response.json();
if (response.status === 401) {
console.log("Please login again");
}
Middleware should return the same standard response format used by the rest of the API.
http_response_code(401);
echo json_encode([
"success" => false,
"message" => "Authentication required",
"data" => null
]);
exit;
This makes error handling easier in React Native.
| Middleware | Responsibility |
|---|---|
| Authentication | Verify user identity |
| Authorization | Check permissions |
| CORS | Handle cross-origin rules |
| Logging | Record useful request information |
| Validation | Check common request requirements |
| Rate Limiting | Limit excessive requests |
Middleware order can matter. For example, authentication must normally run before authorization because authorization needs to know who the user is.
Request
↓
Authentication
↓
Authorization
↓
Validation
↓
Controller
↓
Response
Choose the order according to the requirements of your API.
Question: What is one of the main purposes of authentication middleware?