Headless CMS
Toolkit for Kirby
Keep editing in Kirby and treat it as a headless content backend. Query content via KQL, build custom API routes, and resolve blocks – one config.php for all of it.
Query Content With
KQL
Send Kirby Query Language queries to one endpoint and get JSON back. With Kirby's pages cache on, responses are cached per query and language and flushed on every content change.
- Bearer Token AuthSwitch the endpoint to bearer mode and one token from config.php guards it.
- Response CachingCached per query and language once the pages cache is on; one header bypasses it.
curl https://example.com/api/kql \
-H "Authorization: Bearer your-token" \
-H "Content-Type: application/json" \
-d '{
"query": "page(\"blog\").children.listed",
"select": {
"title": true,
"text": "page.text.toBlocks.toArray",
"url": true
}
}'
{
"code": 200,
"status": "OK",
"result": [
{
"title": "Hello World",
"text": [...],
"url": "https://example.com/blog/hello-world"
}
]
}
Everything Between
Kirby and Your Frontend
Build Custom Routes With the
API Builder
Define API routes with an Express-style middleware pattern. Chain authentication, validation, and response logic.
- Middleware SupportMix your own functions with built-in ones that check the token and body and resolve the language, file, and page.
- Consistent ResponsesApi::createResponse() wraps your data in code, status, and result.
site/config/config.php
use JohannSchopplich\Headless\Api\Api;
use JohannSchopplich\Headless\Api\Middlewares;
return [
'routes' => [
[
'pattern' => 'api/login',
'method' => 'POST',
'action' => Api::createHandler(
Middlewares::hasBearerToken(),
Middlewares::hasBody(...),
function (array $context, array $args) {
$email = $context['body']->get('email');
$user = kirby()->user($email);
return Api::createResponse(200, [
'user' => $user?->email(),
]);
}
),
],
],
];
Open Source – Yours to Extend
Install the plugin and point your frontend at Kirby's API.