🚀 RESTful API Development
Modern API Design with CodeIgniter 4
Building robust RESTful APIs is crucial for modern web applications. CodeIgniter 4 provides excellent tools for API development, especially when paired with PHP 8.2's enhanced features.
🏗️ API Structure Setup
Resource Controller Creation
<?php
namespace App\Controllers\Api;
use CodeIgniter\RESTful\ResourceController;
use CodeIgniter\HTTP\ResponseInterface;
class UsersController extends ResourceController
{
protected $modelName = 'App\Models\UserModel';
protected $format = 'json';
/**
* Get all users
* GET /api/users
*/
public function index(): ResponseInterface
{
$page = (int) $this->request->getGet('page') ?? 1;
$limit = (int) $this->request->getGet('limit') ?? 10;
$search = $this->request->getGet('search');
$users = $this->model
->select('id, username, email, created_at, status')
->when($search, function($query) use ($search) {
return $query->like('username', $search)
->orLike('email', $search);
})
->paginate($limit, 'default', $page);
return $this->respond([
'status' => 'success',
'data' => $users,
'pagination' => [
'current_page' => $page,
'per_page' => $limit,
'total' => $this->model->countAllResults(),
'last_page' => ceil($this->model->countAllResults() / $limit)
]
]);
}
/**
* Get single user
* GET /api/users/{id}
*/
public function show($id = null): ResponseInterface
{
$user = $this->model->select('id, username, email, created_at, status')->find($id);
if (!$user) {
return $this->failNotFound('User not found');
}
return $this->respond([
'status' => 'success',
'data' => $user
]);
}
/**
* Create new user
* POST /api/users
*/
public function create(): ResponseInterface
{
$rules = [
'username' => 'required|min_length[3]|max_length[50]|is_unique[users.username]',
'email' => 'required|valid_email|is_unique[users.email]',
'password' => 'required|min_length[8]'
];
if (!$this->validate($rules)) {
return $this->failValidationErrors($this->validator->getErrors());
}
$data = [
'username' => $this->request->getPost('username'),
'email' => $this->request->getPost('email'),
'password' => password_hash($this->request->getPost('password'), PASSWORD_DEFAULT),
'status' => 'active'
];
$userId = $this->model->insert($data);
if (!$userId) {
return $this->fail('Failed to create user');
}
return $this->respondCreated([
'status' => 'success',
'message' => 'User created successfully',
'data' => ['id' => $userId]
]);
}
/**
* Update user
* PUT /api/users/{id}
*/
public function update($id = null): ResponseInterface
{
if (!$this->model->find($id)) {
return $this->failNotFound('User not found');
}
$rules = [
'username' => "required|min_length[3]|max_length[50]|is_unique[users.username,id,{$id}]",
'email' => "required|valid_email|is_unique[users.email,id,{$id}]"
];
if (!$this->validate($rules)) {
return $this->failValidationErrors($this->validator->getErrors());
}
$data = [
'username' => $this->request->getVar('username'),
'email' => $this->request->getVar('email')
];
if ($this->model->update($id, $data)) {
return $this->respond([
'status' => 'success',
'message' => 'User updated successfully'
]);
}
return $this->fail('Failed to update user');
}
/**
* Delete user
* DELETE /api/users/{id}
*/
public function delete($id = null): ResponseInterface
{
if (!$this->model->find($id)) {
return $this->failNotFound('User not found');
}
if ($this->model->delete($id)) {
return $this->respondDeleted([
'status' => 'success',
'message' => 'User deleted successfully'
]);
}
return $this->fail('Failed to delete user');
}
}
🔐 API Authentication
JWT Token Implementation
<?php
// Install: composer require firebase/php-jwt
namespace App\Controllers\Api;
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
class AuthController extends BaseController
{
public function login()
{
$email = $this->request->getPost('email');
$password = $this->request->getPost('password');
// Validate credentials
$userModel = new \App\Models\UserModel();
$user = $userModel->where('email', $email)->first();
if (!$user || !password_verify($password, $user['password'])) {
return $this->response->setJSON([
'status' => 'error',
'message' => 'Invalid credentials'
])->setStatusCode(401);
}
// Generate JWT token
$key = getenv('JWT_SECRET');
$payload = [
'iss' => base_url(),
'aud' => base_url(),
'iat' => time(),
'exp' => time() + (60 * 60 * 24), // 24 hours
'user_id' => $user['id'],
'username' => $user['username']
];
$token = JWT::encode($payload, $key, 'HS256');
return $this->response->setJSON([
'status' => 'success',
'token' => $token,
'expires_in' => 86400
]);
}
public function validateToken()
{
$token = $this->getTokenFromHeader();
if (!$token) {
return $this->response->setJSON([
'status' => 'error',
'message' => 'Token not provided'
])->setStatusCode(401);
}
try {
$key = getenv('JWT_SECRET');
$decoded = JWT::decode($token, new Key($key, 'HS256'));
return $this->response->setJSON([
'status' => 'success',
'user_id' => $decoded->user_id,
'username' => $decoded->username
]);
} catch (\Exception $e) {
return $this->response->setJSON([
'status' => 'error',
'message' => 'Invalid token'
])->setStatusCode(401);
}
}
private function getTokenFromHeader(): ?string
{
$header = $this->request->getHeader('Authorization');
if ($header && strpos($header->getValue(), 'Bearer ') === 0) {
return substr($header->getValue(), 7);
}
return null;
}
}
📊 API Response Formatting
Custom Response Trait
<?php
namespace App\Traits;
trait ApiResponseTrait
{
protected function apiResponse(
$data = null,
string $message = '',
int $statusCode = 200,
array $meta = []
) {
$response = [
'status' => $statusCode < 400 ? 'success' : 'error',
'message' => $message,
'timestamp' => date('Y-m-d H:i:s'),
];
if ($data !== null) {
$response['data'] = $data;
}
if (!empty($meta)) {
$response['meta'] = $meta;
}
return $this->response
->setJSON($response)
->setStatusCode($statusCode);
}
protected function successResponse($data = null, string $message = 'Success', array $meta = [])
{
return $this->apiResponse($data, $message, 200, $meta);
}
protected function createdResponse($data = null, string $message = 'Created successfully')
{
return $this->apiResponse($data, $message, 201);
}
protected function errorResponse(string $message = 'Error occurred', int $statusCode = 400)
{
return $this->apiResponse(null, $message, $statusCode);
}
protected function validationErrorResponse(array $errors)
{
return $this->apiResponse($errors, 'Validation failed', 422);
}
}
🛡️ Rate Limiting
Throttle Filter
<?php
namespace App\Filters;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
use CodeIgniter\Filters\FilterInterface;
class ThrottleFilter implements FilterInterface
{
public function before(RequestInterface $request, $arguments = null)
{
$cache = \Config\Services::cache();
$key = 'api_throttle_' . $request->getIPAddress();
$attempts = $cache->get($key) ?? 0;
$maxAttempts = 100; // requests per hour
$timeWindow = 3600; // 1 hour in seconds
if ($attempts >= $maxAttempts) {
return \Config\Services::response()
->setJSON([
'status' => 'error',
'message' => 'Rate limit exceeded. Try again later.'
])
->setStatusCode(429);
}
$cache->save($key, $attempts + 1, $timeWindow);
}
public function after(RequestInterface $request, ResponseInterface $response, $arguments = null)
{
// Add rate limit headers
$cache = \Config\Services::cache();
$key = 'api_throttle_' . $request->getIPAddress();
$attempts = $cache->get($key) ?? 0;
$response->setHeader('X-RateLimit-Limit', '100');
$response->setHeader('X-RateLimit-Remaining', max(0, 100 - $attempts));
$response->setHeader('X-RateLimit-Reset', time() + 3600);
}
}
🎯 API Best Practices
- ✅ Use proper HTTP status codes
- ✅ Implement consistent error handling
- ✅ Add API versioning (/api/v1/)
- ✅ Use pagination for large datasets
- ✅ Implement request/response logging
- ✅ Add comprehensive API documentation
📖 API Documentation
Routes Configuration
// app/Config/Routes.php
$routes->group('api/v1', ['namespace' => 'App\Controllers\Api', 'filter' => 'throttle'], function($routes) {
// Authentication
$routes->post('login', 'AuthController::login');
$routes->post('register', 'AuthController::register');
// Protected routes
$routes->group('', ['filter' => 'jwt'], function($routes) {
// Users CRUD
$routes->resource('users', ['controller' => 'UsersController']);
// Custom endpoints
$routes->get('users/(:num)/posts', 'UsersController::getUserPosts/$1');
$routes->post('users/(:num)/avatar', 'UsersController::uploadAvatar/$1');
});
});
#API
#REST
#CodeIgniter4
#WebServices
Building modern APIs with CodeIgniter 4 gives you the flexibility and power needed for today's web applications. Remember to always validate input, handle errors gracefully, and document your API endpoints thoroughly.
Happy API building! 🚀
0 Comments