Make your first API
Revision: 2025/09/20
Create a new file in Modules/MyTestModule.php
Files ending in Module.php or _module.php are loaded automatically.
Use Snake Mode for files that don't contain classes and shouldn't be handled by the autoloader.
To activate a function as an API, you can use attributes. This system makes the code easily readable.
The following example shows three different API types:
- api-test/hello-world: GET, unauthenticated
- api-test/hello-name: POST, authenticated in JWT
- api-test/test-token: POST, with a fixed token.
namespace Modules\MyTestModule;
use App\{Hooks, Get};
use App\Abstracts\{AbstractModule};
use App\Attributes\{ApiEndpoint};
class MyTestModule extends AbstractModule {
#[ApiEndpoint('api-test/hello-world')]
public function helloWorld() {
return $this->success(['message' => 'Hello World']);
}
#[ApiEndpoint('api-test/hello-name', 'POST', ['auth' => true])]
public function helloName() {
$user = Get::make('Auth')->getUser();
return $this->success(['message' => 'Hello ' . $user->username]);
}
#[ApiEndpoint('api-test/test-token', 'POST', ['permissions' => 'token'])]
public function testToken() {
return $this->success(['message' => 'Token is valid']);
}
}
API calls return an array with three elements:
- success: boolean
- message: string
- data: array
{
"success": true,
"message": "Success",
"data": {
"message": "Hello World"
}
}
You can call the first endpoint simply by accessing the link https://milkadmin.org/milk-admin//api.php?page=api-test/hello-world
To call more complex endpoints, you can use the HttpClient class in the framework. You can also download and use this class externally because it has no dependencies.
require __DIR__ . '/App/HttpClient.php';
use App\API;
$api_token = "your token here"; // find token in config.php
$uri = explode('?', $_SERVER['REQUEST_URI']);
$link_complete = $_SERVER['REQUEST_SCHEME'] . '://' . $_SERVER['HTTP_HOST'] . dirname($uri[0])."/";
$response = HttpClient::post($link_complete . 'api.php?page=api-test/hello-world',
['body' => ['token' => $api_token ]]);
To handle authentication, the system uses the JWT system. For the first call, a username and password must be passed. This first call returns a token. The token will then be passed in the header to call the authenticated APIs.
require __DIR__ . '/App/HttpClient.php';
use App\API;
$api_token = "your token here"; // find token in config.php
$uri = explode('?', $_SERVER['REQUEST_URI']);
$link_complete = $_SERVER['REQUEST_SCHEME'] . '://' . $_SERVER['HTTP_HOST'] . dirname($uri[0])."/";
// Example of access with JWT
$link = $link_complete. 'api.php?page=auth/login';
//print "LINK: ".$link."
";
$response = HttpClient::post($link,
['headers' => ['Authorization' => 'Basic' . base64_encode('admin:admin')]]);
print "Test Bearer page=api-test/hello-name:
\n";
if (@$response['status_code'] == 200 && ($response['body']['success'] ?? false)) {
$token = $response['body']['data']['token'];
print "TOKEN: ".$token."
\n";
$response = HttpClient::post($link_complete . 'api.php?page=api-test/hello-name',
['headers' => ['Authorization' => 'Bearer ' . $token]]);
print "";
var_dump ($response['body']);
print "
";
} else {
if ($response['status_code'] != 200) {
print "ERROR : Status code: " . $response['status_code'] . "
\n";
}
if (($response['body']['success'] ?? false) == false) {
print "ERROR : " . $response['body']['message'] . "
\n";
}
}
The Auth module manages 3 endpoints:
- auth/login: to obtain a token
- auth/verify: to verify a token
- auth/refresh: to renew a token
API Documentation with #[ApiDoc]
It is highly recommended to document your APIs using the #[ApiDoc] attribute. This allows automatic documentation generation and better code maintainability.
Add the #[ApiDoc] attribute right after #[ApiEndpoint]:
use App\Attributes\{ApiEndpoint, ApiDoc};
class MyTestModule extends AbstractModule {
#[ApiEndpoint('api-test/hello-world')]
#[ApiDoc(
'Returns a simple hello world message',
[],
['message' => 'string']
)]
public function helloWorld() {
return $this->success(['message' => 'Hello World']);
}
#[ApiEndpoint('api-test/hello-name', 'POST', ['auth' => true])]
#[ApiDoc(
'Returns a personalized greeting for the authenticated user',
['body' => ['name' => 'string']],
['message' => 'string', 'user' => 'string']
)]
public function helloName() {
$user = Get::make('Auth')->getUser();
return $this->success(['message' => 'Hello ' . $user->username]);
}
}
ApiDoc parameters:
- $description: Brief description of what the API does
- $parameters: Array describing input parameters (can be nested)
- $response: Array describing the response structure (can be nested)
Documentation can be accessed programmatically:
use App\API;
// Get documentation for a specific endpoint
$doc = API::getDocumentation('api-test/hello-world');
// List all endpoints with their documentation
$endpoints = API::listEndpoints();