ApiController
The ApiController class extends the base Controller and provides specialized methods for building REST APIs with JSON responses, CORS support, and Bearer token authentication.
Namespace
Extends
Properties
$rate_limiter
Rate limiting configuration (currently set to 1000 requests).
private $rate_limiter = 1000;
Methods
jsonResponse()
Sends a JSON response with the specified HTTP status code.
protected function jsonResponse(array $data, int $statusCode = 200): void
The data to encode and send as JSON
HTTP status code for the response
Example
namespace App\Controllers;
use Sphp\Core\ApiController;
class UserApiController extends ApiController
{
public function getUser($id)
{
$user = $this->db->query('SELECT * FROM users WHERE id = ?', [$id]);
$this->jsonResponse([
'id' => $user[0]['id'],
'name' => $user[0]['name'],
'email' => $user[0]['email']
], 200);
}
}
errorResponse()
Sends a standardized error response in JSON format.
protected function errorResponse(string $message, int $statusCode = 400): void
HTTP status code (400, 401, 403, 404, 500, etc.)
{
"success": false,
"error": "Error message here"
}
Example
public function deleteUser($id)
{
$user = $this->db->query('SELECT * FROM users WHERE id = ?', [$id]);
if (empty($user)) {
$this->errorResponse('User not found', 404);
}
$this->db->query('DELETE FROM users WHERE id = ?', [$id]);
$this->successResponse('User deleted successfully');
}
successResponse()
Sends a standardized success response in JSON format.
protected function successResponse(string $message, array $data = [], int $statusCode = 200): void
Success message to display
Optional data payload to include in the response
HTTP status code (200, 201, 204, etc.)
{
"success": true,
"message": "Success message",
"data": {
// Optional data object
}
}
Example
public function createUser()
{
$name = $_POST['name'];
$email = $_POST['email'];
$this->db->query(
'INSERT INTO users (name, email) VALUES (?, ?)',
[$name, $email]
);
$this->successResponse('User created successfully', [
'id' => $this->db->connection->lastInsertId(),
'name' => $name,
'email' => $email
], 201);
}
Sets CORS (Cross-Origin Resource Sharing) headers for API responses.
protected function setCorsHeaders(): void
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 86400
Automatically handles OPTIONS preflight requests by returning a 200 status code.
Example
public function apiEndpoint()
{
$this->setCorsHeaders();
// Your API logic here
$this->successResponse('Data retrieved', ['items' => $items]);
}
getBearerToken()
Extracts the Bearer token from the Authorization header.
protected function getBearerToken(): ?string
Returns
Returns the token string if found, or null if no Bearer token is present.
Example
public function protectedEndpoint()
{
$token = $this->getBearerToken();
if (!$token) {
$this->errorResponse('No token provided', 401);
}
// Validate and use the token
}
authenticate()
Authenticates the API request using Bearer token authentication with automatic token refresh.
protected function authenticate(): void
Behavior
- Sets CORS headers
- Extracts Bearer token from Authorization header
- Validates the token using
Auth::user()
- If token is invalid/expired, attempts to refresh using
Auth::refresh()
- Sets
$this->user property with decoded user data
- Sends 401 error if authentication fails
Example
namespace App\Controllers;
use Sphp\Core\ApiController;
class ProtectedApiController extends ApiController
{
public function getUserData()
{
// Authenticate the request
$this->authenticate();
// $this->user now contains the authenticated user data
$userId = $this->user['id'];
$data = $this->db->query(
'SELECT * FROM user_data WHERE user_id = ?',
[$userId]
);
$this->successResponse('Data retrieved', ['data' => $data]);
}
}
Complete Usage Example
Here’s a complete example of a REST API controller:
<?php
namespace App\Controllers;
use Sphp\Core\ApiController;
use Sphp\Core\Request;
class ProductApiController extends ApiController
{
// Public endpoint - no authentication required
public function list()
{
$this->setCorsHeaders();
$products = $this->db->query('SELECT * FROM products WHERE active = ?', [1]);
$this->successResponse('Products retrieved', [
'products' => $products,
'count' => count($products)
]);
}
// Protected endpoint - requires authentication
public function create()
{
$this->authenticate(); // Validates Bearer token
$data = Request::request();
if (empty($data['name']) || empty($data['price'])) {
$this->errorResponse('Name and price are required', 400);
}
$this->db->query(
'INSERT INTO products (name, price, user_id) VALUES (?, ?, ?)',
[$data['name'], $data['price'], $this->user['id']]
);
$this->successResponse('Product created successfully', [
'id' => $this->db->connection->lastInsertId()
], 201);
}
// Protected endpoint with parameter
public function update($id)
{
$this->authenticate();
$product = $this->db->query('SELECT * FROM products WHERE id = ?', [$id]);
if (empty($product)) {
$this->errorResponse('Product not found', 404);
}
$data = Request::request();
$this->db->query(
'UPDATE products SET name = ?, price = ? WHERE id = ?',
[$data['name'], $data['price'], $id]
);
$this->successResponse('Product updated successfully');
}
// Protected endpoint - delete
public function delete($id)
{
$this->authenticate();
$product = $this->db->query('SELECT * FROM products WHERE id = ?', [$id]);
if (empty($product)) {
$this->errorResponse('Product not found', 404);
}
$this->db->query('DELETE FROM products WHERE id = ?', [$id]);
$this->successResponse('Product deleted successfully', [], 204);
}
}
API Routes
Define API routes in app/router/api.php:
<?php
use App\Controllers\ProductApiController;
use Sphp\Core\Router;
$api = new Router();
// Public routes
$api->get('/api/products', ProductApiController::class, 'list');
// Protected routes
$api->post('/api/products', ProductApiController::class, 'create');
$api->post('/api/products/{id}', ProductApiController::class, 'update');
$api->post('/api/products/delete/{id}', ProductApiController::class, 'delete');
$api->dispatch();
Client Usage
Making Authenticated Requests
// Login to get token
const loginResponse = await fetch('http://localhost:8000/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
email: '[email protected]',
password: 'password123'
})
});
const { access_token } = await loginResponse.json();
// Use token for authenticated requests
const response = await fetch('http://localhost:8000/api/products', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${access_token}`
},
body: JSON.stringify({
name: 'New Product',
price: 99.99
})
});
const result = await response.json();
console.log(result); // { success: true, message: "Product created successfully", data: { id: 123 } }
Best Practices
- Always use jsonResponse methods: Use
successResponse(), errorResponse(), or jsonResponse() instead of manual JSON encoding
- Call authenticate() for protected endpoints: Ensures proper token validation and user authentication
- Set CORS headers for public APIs: Call
setCorsHeaders() at the start of public endpoints
- Use proper HTTP status codes: 200 (OK), 201 (Created), 400 (Bad Request), 401 (Unauthorized), 404 (Not Found), 500 (Server Error)
- Include meaningful error messages: Help API consumers understand what went wrong
- Return consistent response formats: Use the standard success/error response structure