API
Revision: 2025-11-11
RESTful API system with JWT authentication, automatic routing, and endpoint documentation.
Response Format
All API responses follow a standardized JSON format:
Success responses:
{
"success": true,
"data": { /* your data */ }
}
Error responses:
{
"success": false,
"message": "Error description"
}
Endpoint Registration
In modules (extends AbstractApi or extended AbstactModule):
class MyTestApi extends AbstractApi {
#[ApiEndpoint('my-test/hello')]
public function apiHello($request) {
return $this->success(['message' => 'Hello World']);
}
}
With authentication and HTTP method:
class UsersApi extends AbstractApi {
#[ApiEndpoint('users/create', 'POST', ['auth' => true])]
public function apiCreate($request) {
$data = $request['body'];
return $this->success(['id' => 1, 'username' => $data['username']]);
}
}
Manual registration:
use App\API;
API::set('test/hello', function($request) {
return ['message' => 'Hello World'];
});
Documenting APIs
Add documentation using the #[ApiDoc] attribute:
use App\Attributes\{ApiEndpoint, ApiDoc};
class PostsApi extends AbstractApi {
#[ApiEndpoint('posts/create', 'POST', ['auth' => true])]
#[ApiDoc(
'Create a new blog post',
['body' => ['title' => 'string', 'content' => 'string']],
['id' => 'int', 'title' => 'string', 'created_at' => 'datetime']
)]
public function createPost($request) {
return $this->success(['id' => 1, 'title' => 'New Post']);
}
}
With API::set():
API::set('test/hello',
function($request) {
return ['message' => 'Hello World'];
},
[], // Options
'Returns a hello world message', // Description
[], // Parameters
['message' => 'string'] // Response structure
);
Exception Handling
Throw exceptions in your API handlers - the framework catches and formats them automatically:
use App\Exceptions\{ApiException, ApiAuthException};
API::set('users/delete', function($request) {
$id = $request['input']('id');
if (!$id) {
throw new ApiException("User ID required", 422);
}
$user = User::find($id);
if (!$user) {
throw new ApiException("User not found", 404);
}
if ($user->id !== API::user()->id) {
throw new ApiAuthException("Cannot delete other users", 403);
}
$user->delete();
return ['success' => true];
}, ['auth' => true, 'method' => 'DELETE']);
Available exceptions:
ApiException- General API errors (default 400)ApiAuthException- Authentication/authorization errors (default 403)
Methods
API::set(string $page, callable|string $handler, array $options = [], ?string $description = null, ?array $parameters = null, ?array $response = null) : void
Registers an API endpoint.
API::set('users/list', function($request) {
return ['users' => User::all()];
});
API::group(array $options, callable $callback) : void
Groups endpoints with common options.
API::group(['prefix' => 'admin', 'auth' => true], function() {
API::set('users', 'AdminModule@users');
API::set('settings', 'AdminModule@settings');
});
API::run(string $page) : bool
Executes an API endpoint. Handles authentication and exceptions automatically.
API::user() : ?object
Returns the currently authenticated user or null.
API::payload() : ?array
Returns the current JWT payload or null.
API::request() : ?array
Returns the current request data.
API::generateToken(int $user_id, array $additional_data = []) : array
Generates a JWT token for a user.
API::refreshToken() : array
Refreshes the current JWT token.
API::successResponse(mixed $data) : void
Sends a success response.
API::errorResponse(string $msg, int $status, ?array $debug_info = null) : void
Sends an error response.
API::listEndpoints() : array
Returns all registered endpoints with their documentation.
API::getDocumentation(string $page) : ?array
Returns documentation for a specific endpoint.
Request Structure
Handlers receive a request array with structured data:
function($request) {
// HTTP method
$method = $request['method'];
// Endpoint page
$page = $request['page'];
// URL parameters (id, slug, etc.)
$id = $request['params']['id'] ?? null;
// Query parameters (?key=value)
$search = $request['query']['search'] ?? '';
// Request body (JSON or form data)
$data = $request['body'];
// Headers
$contentType = $request['headers']['Content-Type'] ?? '';
// Uploaded files
$files = $request['files'];
// Auth info (if authenticated)
$user = $request['auth']['user'] ?? null;
// Helper methods
$value = $request['input']('key', 'default');
$exists = $request['has']('key');
$all = $request['all']();
}
Options
Authentication:
['auth' => true]- Require JWT authentication
Permissions:
['permissions' => 'permission.name']- Require specific permission['permissions' => 'token']- Require fixed API token fromConfig::get('api_token')
HTTP Methods:
['method' => 'GET']- Only allow GET requests['method' => 'POST']- Only allow POST requests['method' => 'PUT']- Only allow PUT requests['method' => 'DELETE']- Only allow DELETE requests['method' => 'ANY']- Allow any method (default)
Authentication Endpoints
auth/login
POST - Obtain JWT token with credentials.
curl -X POST "api.php?page=auth/login" \
-H "Content-Type: application/json" \
-d '{"username": "admin", "password": "admin"}'
{
"success": true,
"result": {
"user": {
"id": 1,
"username": "admin",
"email": "admin@example.com"
},
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9...",
"expires_at": 1641234567,
"expires_in": 3600
}
}
auth/verify
GET - Verify token validity.
curl -X GET "api.php?page=auth/verify" \
-H "Authorization: Bearer YOUR_TOKEN"
{
"success": true,
"result": {
"user_id": 1,
"username": "admin",
"expires_at": 1641234567
}
}
auth/refresh
GET - Renew token with existing valid token.
curl -X GET "api.php?page=auth/refresh" \
-H "Authorization: Bearer YOUR_TOKEN"
{
"success": true,
"result": {
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9...",
"expires_at": 1641237567,
"expires_in": 3600
}
}
Error Responses
All errors return HTTP status codes and a standardized error format:
Authentication error (401):
{
"success": false,
"message": "No authentication token provided"
}
Permission error (403):
{
"success": false,
"message": "Insufficient permissions"
}
Not found (404):
{
"success": false,
"message": "Endpoint not found"
}
Method not allowed (405):
{
"success": false,
"message": "Method POST not allowed. Expected: GET"
}
Server error (500):
{
"success": false,
"message": "Internal server error"
}
Examples
Complete Authenticated API
use App\API;
use App\Exceptions\ApiException;
API::set('posts/create',
function($request) {
$title = $request['body']['title'];
if (empty($title)) {
throw new ApiException("Title required", 422);
}
// Get authenticated user
$user = API::user();
$post = new PostsModel();
$post->fill([
'title' => $title,
'content' => $request['body']['content'],
'user_id' => $user->id
]);
$post->save();
return [
'id' => $post->id,
'title' => $post->title,
'created_at' => $post->created_at
];
},
['auth' => true, 'method' => 'POST'],
'Create a new blog post',
['body' => ['title' => 'string', 'content' => 'string']],
['id' => 'int', 'title' => 'string', 'created_at' => 'datetime']
);
PHP Client Example
use App\HttpClient;
use App\Route;
// Login
$response = HttpClient::post(Route::url() . '/api.php?page=auth/login', [
'headers' => ['Content-Type' => 'application/json'],
'body' => json_encode(['username' => 'admin', 'password' => 'admin'])
]);
if ($response['status_code'] == 200 && $response['body']['success']) {
print "Success
";
print "TOKEN: " . $response['body']['data']['token'] . "";
$user = $response['body']['data']['user'];
echo "Username: " . $user['username'];
} else {
print "Error
";
echo "ERROR: " . $response['body']['message'] . "";
}