Kirby Headless Starter
ℹ️ Send aBearer test
authorization header with a request to the live playground to test this headless starter.
This starter kit is intended for an efficient and straight forward headless usage of Kirby. Thus, you will only be able to fetch JSON-encoded data. No visual data shall be presented.
Routing and JSON-encoded responses are handled by the internal routes.
Key Features
-
🦭 Optional bearer token for authentication -
🧩 KQL with bearer token support- Post requests to
/query
- Post requests to
-
⚡️ Cached KQL results -
🗂 Templates present JSON instead of HTML- Fetch either
/example
or/example.json
- Fetch either
-
🦾 Express-esque API builder with middleware support
Prerequisites
- PHP 8.0+
Kirby is not a free software. You can try it for free on your local machine but in order to run Kirby on a public server you must purchase a valid license.
Setup
Composer
Kirby-related dependencies are managed via Composer and located in the vendor
directory. To install them, run:
composer install
Environment Variables
Duplicate the .env.development.example
as .env
:
cp .env.example .env
Optionally, adapt it's values.
Usage
Bearer Token
If you prefer to use a token to secure your Kirby installation, set the environment variable KIRBY_HEADLESS_API_TOKEN
with a token of your choice.
You will then have to provide the header Bearer ${token}
with each request.
⚠️ Without a token the/query
route would be publicly accessible by everyone. Be careful.
Data Fetching
You can fetch data by using KQL or Templates.
Templates
Create templates just like you normally would in any Kirby project. Instead of writing HTML, we build arrays and encode them to JSON. The internal headless plugin will add the correct content type and also handles correct caching.
Example template:
# /site/templates/about.php
$data = [
'title' => $page->title()->value(),
'layout' => $page->layout()->toLayouts()->toArray(),
'address' => $page->address()->value(),
'email' => $page->email()->value(),
'phone' => $page->phone()->value(),
'social' => $page->social()->toStructure()->toArray()
];
echo \Kirby\Data\Json::encode($data);
KQL
ℹ️ Keep in mind the KQL endpoint/api/query
remains and uses the username/password authentication.
KQL is available under /query
and requires a bearer token set.
import { $fetch } from "ohmyfetch"
const apiToken = "test"
const response = await $fetch(
"https://example.com/query",
{
method: 'POST'
body: {
query: "page('notes').children",
select: {
title: true,
text: "page.text.toBlocks.toArray",
slug: true,
date: "page.date.toDate('d.m.Y')",
},
},
headers: {
Authentication: `Bearer ${apiToken}`,
},
},
);
console.log(response);
API Builder
This headless starter includes an Express-esque API builder, defined in the KirbyHeadless\Api\Api
class. You can use it to re-use logic like handling a token or verifying some other incoming data.
Take a look at the built-in routes to get an idea how you can use the API builder to chain complex route logic.
It is also useful to consume POST requests including JSON data:
# /site/config/routes.php
[
'pattern' => 'post-example',
'method' => 'POST',
'action' => Api::createHandler(
[Middlewares::class, 'hasBearerToken'],
[Middlewares::class, 'hasBody'],
function ($context) {
// Get the data of the POST request
$data = $context['body'];
// Do something with `$data` here
return Api::createResponse(201);
}
)
]
You you use one of the built-in middlewares or write custom ones:
/**
* Check if `foo` is sent with the request
* and bail with an 401 error if not
*
* @param array $context
* @return mixed
*/
public static function hasFooParam($context)
{
if (empty(get('foo'))) {
return Api::createResponse(401);
}
}
Deployment
ℹ️ See ploi-deploy.sh for exemplary deployment instructions.
ℹ️ Some hosting environments require to uncommentRewriteBase /
in.htaccess
to make site links work.
Background
Why Not Use Content Representations?
Content representations are great. But they require a non-representing template. Otherwise, the content representation template just would not be read by Kirby. This means, you would have to create the following template structure:
default.php
default.json.php
home.php
home.json.php
- … and so on
To simplify this approach, we use the standard template structure, but encode each template's content as JSON via the internal route middleware.
License
MIT License © 2022 Johann Schopplich