Milk Admin

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.

By convention, if you use Pascal Mode for classes, the file name must be the same.
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
In this first example, we used the module to manage the endpoints. However, it is possible create a new file in the module folder (you must create a folder to hold the module files) that has the same name as the MyTestApi.php module.

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();
Loading...