Introduction
The Laravel CMS JSON frontend API follows the JSON:API standard documented at jsonapi.org and is available at (replace “mydomain.tld” with your own one):
http://mydomain.tld/api/cms/pages
The pages
endpoint will return items from the page tree as well as related shared content elements depending on parameters added.
Available properties
Page properties
The available page properties are:
{
"type": "pages",
"id": "1",
"attributes": {
"parent_id": null,
"lang": "",
"slug": "",
"name": "Home",
"title": "Home | Laravel CMS",
"tag": "root",
"to": "",
"domain": "mydomain.tld",
"cache": 5,
"data": [
{
"text": "Welcome to Laravel CMS",
"type": "cms::heading"
}
],
"meta": {
"cms::meta": {
"text": "Laravel CMS is outstanding",
"type": "cms::meta"
}
},
"config": null,
"createdAt": "2023-03-12T16:06:26.000000Z",
"updatedAt": "2023-03-12T16:06:26.000000Z"
}
}
- parent_id
- ID of the parent page
- lang
- ISO language code, either two letters in lower case (e.g. “en”) or five characters for country specific languages (e.g. “en_US”)
- slug
- URL segment of the page (must not contain any slashes)
- name
- Short page name for navigation
- title
- Page title like shown in the browser
- tag
- Arbitrary name which can be used for filtering to get a specific page
- to
- URL to the target page in case the page is redirecting to another page
- domain
- Domain name the (root) page is responsible for
- cache
- How long the returned response (and therefore the generated page can be cached
- meta
- Set of arbitrary page meta data that should be part of the page head
- data
- List of arbitrary page content elements that should be part of the page body
- config
- Arbitrary key/value pairs with page configuation
- createdAt
- ISO date/time when the page was created
- updatedAt
- ISO date/time when the page was last modified
Shared content properties
The available properties of content shared between pages are:
{
"type": "contents",
"id": "0186d692-be0b-798c-9450-0a676209b7a6",
"attributes": {
"lang": "",
"data": {
"text": "Welcome to Laravel CMS",
"type": "cms::heading"
},
"createdAt": "2023-03-12T16:06:26.000000Z"
}
}
- lang
- ISO language code, either two letters in lower case (e.g. “en”) or five characters for country specific languages (e.g. “en_US”)
- data
- Arbitrary content element that should be part of the page body
- createdAt
- ISO date/time when the content element was created
URL parameters
Filter results
To limit the returned items exactly to the ones you want, the JSON API supports one or more filter
parameters:
- domain
- Filters the pages by domain name which must be set at least in the root page in the
domain
property - tag
- Returns only pages (or one page) where the passed value is set in the
tag
property of the page item - lang
- In case of multi-language page trees, this parameter limits the pages to the specified language set in the
lang
property of the page item
To get the page tagged with root
for the domain mydomain.tld
in English, use:
http://mydomain.tld/api/cms/pages?filter[tag]=root&filter[domain]=mydomain.tld&filter[lang]=en
Include resources
When including related resources, you can get all data you need to render the page including the navigation in one request. The available related resources are:
- contents
- List of shared content elements for the requested page (paginated if more than 50 items)
- parent
- Parent page item
- ancestors
- All parent pages up to the root page
- children
- List of direct child pages for the requested page (paginated if more than 15 items)
- subtree
- Tree of sub-pages up to three levels deep for building a mega-menu
To get the page tagged with blog
including its ancestors and shared content elements use:
http://mydomain.tld/api/cms/pages?filter[tag]=blog&include=ancestors,contents
There are detailed examples for the most often used requests available:
Pagination
You can paginate through the results by adding the page
parameter to the /api/cms/pages
URL. The supported values are:
- number
- Number of the slice that should be fetched starting from “1” up to the total number of available pages (see the pagination respone for details)
- size
- Number of items that should be fetched with a minimum value of “1” and a maximum value of “100”. The default values are “15” for pages and “50” for contents
To get item 25 to 50 from the pages
endpoint use:
http://mydomain.tld/api/cms/pages?page[number]=2&page[size]=25
This can be combined with filter
and include
parameters too:
http://mydomain.tld/api/cms/pages?filter[lang]=en&include=contents&page[number]=2&page[size]=25
In the last case, use the link instead of constructing the URL yourself!
Sparse fields
Most often, you don’t need all page or shared content properties and you can reduce the amount of data returned in the response by using the fields
parameter. The requested fields can be limited for pages and shared content elements separately and the property names must be concatenated by comma.
To retrieve the slug
and lang
of the root pages only and the data
property of the shared content elements, use:
http://mydomain.tld/api/cms/pages?include=contents&fields[pages]=slug,lang&fields[contents]=data
Then, the attributes of the returned pages in the data section will contain only:
"data": [
{
"type": "pages",
"id": "1",
"attributes": {
"lang": "",
"slug": ""
},
"links": {
"self": "http:\/\/mydomain.tld\/api\/cms\/pages\/1"
}
}
]
The type
and id
of each item is always returned outside the attributes
and can’t be skipped!
Responses
In the JSON-encoded response, there are three sections which are important:
Meta
Base URL
The meta section always contains the baseurl
key which is the base URL to all files/images referenced by the page or the shared content elements. Typically, you will see this in most responses:
"meta": {
"baseurl": "http:\/\/mydomain.tld\/storage\/"
}
In Laravel, you can change the base URL in the ./config/filesystems.php
file where you need to change the url
setting for the disk
Laravel CMS is using (public
by default).
Paged results
Responses which returns a collection of pages (/api/cms/pages
), you will also notice a page
key in the meta
section which contains the pagination information:
"meta": {
"page": {
"currentPage": 1,
"from": 1,
"lastPage": 1,
"perPage": 15,
"to": 1,
"total": 1
}
}
Important key/value pairs are:
- currentPage
- Page number of the current page (starts with “1”)
- lastPage
- Page number of the last page available when using the same
perPage
value. Minimum value is “1”, the maximum value is “100” - perPage
- Maximum number of items returned in one response which is the passed
page[size]
value. The minimum value is “1”, the maximum value is “100” and the default values are “15” for pages and “50” for contents - total
- Total number of available pages when using the same
size
value
Links
The links
section in the JSON API response is always included and contains the self
link which would return the same response again:
"links": {
"self": "http:\/\/mydomain.tld\/api\/cms\/pages\/1"
},
Thus, you can always use the links to fetch data and don’t have to construct the links yourself!
Data
The data
section of the JSON:API response contains either a single resource (in case of e.g. /api/cms/pages/1
) or a collection of resources (for /api/cms/pages
).
Single item
Using a request which returns a single page, then the response is like:
"data": {
"type": "pages",
"id": "1",
"attributes": {
"parent_id": null,
"lang": "",
"slug": "",
"name": "Home",
"title": "Home | Laravel CMS",
"tag": "root",
"to": "",
"domain": "mydomain.tld",
"has": true,
"cache": 5,
"data": [
{
"text": "Welcome to Laravel CMS",
"type": "cms::heading"
}
],
"meta": {
"cms::meta": {
"text": "Laravel CMS is outstanding",
"type": "cms::meta"
}
},
"config": null,
"createdAt": "2023-05-01T09:36:30.000000Z",
"updatedAt": "2023-05-01T09:36:30.000000Z"
},
"relationships": {
"contents": {
"data": [
{
"type": "contents",
"id": "0187d6ab-b76d-75ee-8830-ab00b4259aa5"
}
]
}
},
"links": {
"self": "http:\/\/localhost:8000\/api\/cms\/pages\/1"
}
},
The data
section contains exactly one object with type
and id
properties which uniquely identifies the resource. Within the attributes
part, the page properties are listed like shown above but could be also less if you’ve requested only specific fields. In the links
part, the self
URL to retrieve the same page data is included. The relationships
part is described in the relationships section of this document.
Multiple items
For request returning multiple items, the data
section will be similar to:
"data": [
{
"type": "pages",
"id": "1",
"attributes": {
"parent_id": null,
"lang": "",
"slug": "",
"name": "Home",
"title": "Home | Laravel CMS",
"tag": "root",
"to": "",
"domain": "mydomain.tld",
"has": true,
"cache": 5,
"data": [
{
"text": "Welcome to Laravel CMS",
"type": "cms::heading"
}
],
"meta": {
"cms::meta": {
"text": "Laravel CMS is outstanding",
"type": "cms::meta"
}
},
"config": null,
"createdAt": "2023-05-01T09:36:30.000000Z",
"updatedAt": "2023-05-01T09:36:30.000000Z"
},
"relationships": {
"contents": {
"data": [
{
"type": "contents",
"id": "0187d6ab-b76d-75ee-8830-ab00b4259aa5"
}
]
}
},
"links": {
"self": "http:\/\/localhost:8000\/api\/cms\/pages\/1"
}
},
// ...
],
It’s the same like for responses returning single resources but the data
section contains a list of page items.
Relationships
If you use the include parameter to get related resources in the same request there will be a key for each related resource below relationships
.
For a request which should include the parent page, ancestor pages, child pages, the page subtree and the contents like:
http://mydomain.tld/api/cms/pages/1?include=parent,ancestors,children,subtree,contents
Then, the relationships
section will contain:
"relationships": {
"contents": {
"data": [
{
"type": "contents",
"id": "0187d6ab-b76d-75ee-8830-ab00b4259aa5"
}
]
},
"parent": {
"data": null
},
"children": {
"data": [
{
"type": "pages",
"id": "2"
},
{
"type": "pages",
"id": "4"
},
{
"type": "pages",
"id": "5"
}
]
},
"ancestors": {
"data": []
},
"subtree": {
"data": [
{
"type": "pages",
"id": "2"
},
{
"type": "pages",
"id": "3"
},
{
"type": "pages",
"id": "4"
},
{
"type": "pages",
"id": "5"
}
]
}
},
Each key in the relationships
part will a reference to a single item (like for parent
) or references to multiple items in their data
sections. The items themselves will be part of the included
section of the returned response. In case of the root page, the parent/data
key can also be NULL because there’s no parent page for the root page any more:
"relationships": {
"parent": {
"data": null
}
},
Included
The included
section of each JSON API response is only available if you’ve added the include
parameter to the URL, e.g. /api/cms/pages/1?include=contents
. In that case the relationships/contents/data
part contains the list of references:
{
"type": "pages",
"id": "1",
"attributes": {
"name": "Home",
"title": "Home | Laravel CMS",
"tag": "root",
"more keys": "..."
},
"relationships": {
"contents": {
"data": [
{
"type": "contents",
"id": "0186d692-be0b-798c-9450-0a676209b7a6"
}
]
}
}
}
And the included
section for that response then contains:
"included": [
{
"type": "contents",
"id": "0186d692-be0b-798c-9450-0a676209b7a6",
"attributes": {
"lang": "",
"data": {
"text": "Welcome to Laravel CMS",
"type": "cms::heading"
},
"created_at": "2023-03-12T16:06:26.000000Z"
}
}
]
It consists of a flat list of page or shared content items identified by their type
and id
values. You must now match the type and ID within the relationships/contents
section with the type and ID within the included
section.
Error handling
Errors can and will occur sooner or later. The JSON:API standard like every REST protocol uses the HTTP status codes to signal error conditions. Used HTTP status codes are:
- 2xx : Successful operation
- 200 : Operation was performed successfully
- 201 : Resource has been created
- 4xx : Bad request
- 401 : Authentication required
- 403 : Operation is forbidden/unsupported
- 404 : The resource wasn’t found
- 5xx : Internal server error
- 500 : A non-recoverable error occurred
- 501 : Operation not implemented
Also, the JSON API standard specifies an “errors” section in the JSON response that can contain error hints for one or more operations:
{
"errors": [
{
"title": "No product with ID 1 available",
"detail": "<stack trace where the error occured>"
},
...
]
}
Each error item contains a “title” attribute that contains the error message for the user and the “detail” attribute including the stack trace for developers. You should show the error details because they are only helpful for developers:
const promise = fetch('/api/cms/pages?...', {
method: 'GET',
credentials: 'same-origin',
}).then(response => {
if(!response.ok) {
throw new Error(response.statusText)
}
return response.json();
}).then(result => {
if(result.errors) {
throw result.errors
}
return result
}).catch(err => {
console.error(err)
})
Comments