Headless allows you to render JSON from TYPO3 content. You can customize output
by changing types, names and nesting of fields.
This extension provides the backend part (JSON API) for TYPO3 PWA solution.
The frontend part exists as JavaScript application
nuxt-typo3
which consumes the JSON API and renders the content using Nuxt framework of VueJS.
You can find the frontend documentation here.
If you have any questions just drop a line in our #initiative-pwa Slack channel.
Special thanks goes to macopedia.com company, which is sponsoring development of this solution.
TYPO3
The content of this document is related to TYPO3 CMS,
a GNU/GPL CMS/Framework available from typo3.org .
For Contributors
You are welcome to help improve this guide if you missing something.
Just click on "Edit me on GitHub" on the top right to submit your change request
or report a problem
This extension is designed to facilitate seamless integration between TYPO3 and modern frontend frameworks by providing a robust and flexible headless CMS solution. Its stable release, it ensures reliable performance and multiple features.
What does it do?
The headless extension provides a JSON API that serves as an endpoint for various types of applications. It utilizes standard TYPO3 features to render the page tree structure and page content into JSON format. The JSON response object and content elements can be customized using TypoScript.
Features
JSON API for content elements
JSON API for navigation page tree structure
Comprehensive support for language and translation configurations (e.g., fallback)
Easy extensibility with custom fields or custom Content Elements (CEs)
Integration support for EXT:felogin and EXT:form
Integration support for EXT:news (see additional extension EXT:headless_news)
Performance Optimization: The headless architecture enhances performance by delivering only the necessary data to the client, reducing load times and improving the user experience.
Scalability: Easily scale your application by decoupling the backend from the frontend, allowing for independent scaling based on demand.
Flexibility: Provides the flexibility to integrate with various frontend frameworks and libraries, enabling developers to choose the best tools for their specific use case.
Future-Proofing: Stay ahead of the curve with a modern headless CMS approach, ensuring your application remains relevant and adaptable to future technological advancements.
This extension is designed to empower developers to build dynamic, responsive, and high-performing web applications while leveraging the powerful features of TYPO3. Whether you are building a single-page application (SPA), a progressive web app (PWA), or any other type of frontend application, this extension provides the tools you need to succeed.
Installation
To install and configure the headless extension, follow these detailed steps. First, ensure TYPO3 is installed and running (refer to t3install:start for the standard TYPO3 installation process). Once TYPO3 is operational, proceed with the following steps to install the headless extension:
Install and activate the extension:
- Using Composer (recommended): Run the following command in your terminal:
``
composer require friendsoftypo3/headless
`
- Alternatively, use the Extension Manager with the extension key headless`.
Create a new root page:
- This page will serve as the JSON API endpoint for your application.
- Open the TYPO3 backend, navigate to the page tree, and create a new page at the root level. Name this page appropriately (e.g., "API Root").
Create a new root template:
- In the TYPO3 backend, go to Site Management > TypoScript (or Web > Template for versions prior to v12).
- Add the headless static template by including it in the "Include static (from extensions)" section.
- Save your changes. You should now be able to see the JSON output when displaying the page.
Create a site configuration:
- The site configuration is essential as it defines the URL that will serve as the API endpoint for the frontend application.
- In TYPO3 v10 and above, an autogenerated site configuration may already exist. You can rename and configure this site to suit your needs.
Important
It is recommended to use a dedicated URL like https://api.mydomain.org for the endpoint. Using URLs such as https://mydomain.org/api might lead to unexpected behaviors.
Configuration
Feature Flags
To modify the settings for this extension, you can use either LocalConfiguration.php or AdditionalConfiguration.php.
headless.storageProxy
Enables the ability to set a storage proxy in the site configuration (and its variants) & serve files via proxy from the same domain.
Feature flag requires TYPO3 >= 10.4.10.
WARNING If you install TYPO3 >= 10.4.18, please update ext:headless to version >= 2.5.3.
Enables clean output middleware for plugins. Clean output is available for POST/PUT/DELETE method requests. To get clean output for plugins on a page, enable this flag and add headless to the site configuration, then send the responseElementId field with the ID of the plugin in the body with plugin data.
Add the following flag to your site configuration's YAML file:
headless:true
Copied!
Example POST request with plugin form. Replace #ELEMENT_ID# with the ID of the plugin from the page response.
POST https://example.tld/path-to-form-plugin
Content-Type: application/x-www-form-urlencoded
responseElementId=#ELEMENT_ID#&tx_form_formframework[email]=email&tx_form_formframework[name]=test...
Copied!
To find a nested element, use the new flag responseElementRecursive, where responseElementId is the child (nested element). Example request:
POST https://example.tld/path-to-form-plugin
Content-Type: application/x-www-form-urlencoded
responseElementId=#ELEMENT_ID#&responseElementRecursive=1&tx_form_formframework[email]=email&tx_form_formframework[name]=test...
Simplified output returns only the value, e.g., _blank for the target attribute instead of the HTML string target="_blank".
headless.jsonViewModule
Available since 3.0.
Enables the experimental JsonView backend module, which allows previewing the page JSON response in the backend module when passing specific pageType, pageArguments, usergroups, language.
This flag requires an additional extension friendsoftypo3/headless-dev-tools.
If EXT:form is enabled in the TYPO3 instance, EXT:headless provides support for handling forms in headless mode.
Standard forms designed in the form editor in TYPO3 backend should work out of the box, but the headless extension supports additional small tweaks/features to help frontend developers better handle forms on their end.
All options are added in YAML files with standard form configuration in TYPO3.
i18n
In many cases in headless mode, frontend developers need some translated strings for common elements like buttons, help messages, etc.
With EXT:headless, you can add additional configuration in the root line of the form config:
i18n:identifier:'i18n'properties:someButtonLabel:'Submit or Cancel'someHelpMessage:'You need to fill out this form'requiredFields:'These fields are required'
Copied!
The above block will be automatically translated by provided XLF files like a standard form in fluid.
This block will be translated & available in the "i18n" part of the response. More about form output can be found in the Form Decorator section.
Form Decorator
The headless extension provides an out-of-the-box simple decorator for form definition output. The decorator simplifies the response and provides an API to customize your response for your specific needs.
In the rendering options of the form, you can define your custom project/form decorator. If the option is not defined, the headless extension defaults to:
To help frontend developers create validation handling in a frontend context, you can add small tweaks to form element definitions to ease development for your frontend team.
In the form element definition, you can add an option to errorMessage for your defined validators with the error code value. This code will be picked up and translated by standard TYPO3 XLF form files.
When creating a RegexValidator, there are some differences when handling regular expressions by PHP & JS. To help frontend developers create consistent frontend/backend validation, we introduced a small option for regex validators in TYPO3.
If the headless form decorator finds the option FERegularExpression in the validator definition, it will override options.regularExpression with the value of the FERegularExpression option before sending the output to the frontend developer.
Custom Options
When you need a select/radio/checkbox with custom options fetched from, for example, a database or another external source, you need to create a Custom FormModel. In headless mode, we do not render HTML and render all the options, so we introduced a small interface:
When the above option is set with a class that implements the correct interface, the options of the select element will be replaced by the values returned by the specified class.
To make rendering of the element easier for frontend developers, we introduced the option to override the type returned to the frontend developer. For example, when you set FEOverrideType in the renderingOptions of a custom element:
We use this value to override the type, so the response to the frontend developer will be:
{"type":"Select"}
Copied!
instead of:
{"type":"SingleSelectWithCountryList"}
Copied!
JSON Redirect
EXT:headless supports handling finishers. For example, after handling correctly sent form data, you can use TYPO3 core's RedirectFinisher to redirect to a thank you page. To have more control on the frontend side, we provide in the headless extension:
JsonRedirectFinisher
This is based on the core RedirectFinisher but, instead of delay & statusCode options, has an option for a message that can be handled by the frontend developer to display a message for the user before redirecting to the defined page.
Also, JsonRedirect does not redirect by itself but generates a message (default is null) and URI for redirection by the frontend developer.
To use JsonRedirect, define it in the setup.yaml of your extension form's setup:
The JsonView module is an experimental approach for previewing JSON responses of a page in different contexts like pageType, page arguments, usergroup, language, and show/hide hidden content.
!WARNING This is an experimental module, please don't use it on a production environment at this time.
PageTypeModes
You can set the context in which you want to preview a page.
By default, there are 3 settings available:
default - standard response with page data and content
initialData - standard response from pageType=834
detailNews (commented out) - example of calling the detail action of the news extension for test purposes
You can always create your own YAML configuration and set it in the extension configuration.
Content element categories
Headless default configuration for content element categories in TypoScript lib.contentElement is to look in root (page uid=0). However depending on your project needs this may not ideal.
(as of TYPO3 v12 you cannot use together pidInList = root and recursive = 99)
Alternative approach for categories may be to use categories from current rootPage which mitigates this problem.
To begin you need to unset previous pidInList value
How to configure your application to preview hidden pages?
The headless extension supports the preview functionality for hidden pages. However, proper configuration is required to ensure your application works as expected.
Since there is no concept of cross-domain cookies, ensure that both your backend and frontend servers share the same root domain (e.g., backend: api.domain.com, frontend: domain.com). Then, set the root domain as cookieDomain (note the dot at the beginning).
If you are logged into the backend during this change (after deployment), you may need to remove the be_typo_user cookie from your browser. The old cookie will conflict with the new configuration, preventing you from logging in.
If your frontend application correctly passes all cookies from the backend, you should be able to preview content associated with the root domain.
Note
If you have a multi-domain setup, e.g., api.domain1.com, domain1.com and api.domain2.com, domain2.com, this solution will not work out-of-the-box. You will need to override TYPO3_CONF_VARS dynamically. This hasn't been extensively tested, but theoretically, you could achieve this by running custom middleware as the first one in the stack.
(Optional) Make the backend application available via a proxy, e.g., domain.com/headless. This step is not strictly necessary for the application to run but can help resolve various issues in the long run.
XML Sitemap
Since ext:headless 4.0 there's no custom code regarding the XML sitemaps
apart from the templates used to render the XML sitemap's source code.
Troubleshooting
In case the URLs listed in the "sitemap index" file (usually /sitemap.xml)
aren't "frontend URLs" (but their API variant), consider setting one of the two
following configurations:
either frontendApiProxy being defined in the site's config.yaml
or this in the site's settings.yaml:
headless:sitemap:key:frontendBase
Copied!
Developer
This chapter will explain different usecases for developer working with headless extension.
New cObjects
EXT:headless comes with a bunch of new cObjects to be used via TypoScript:
BOOL
FLOAT
INT
JSON
CONTENT_JSON
BOOL, FLOAT and INT are basically like TEXT (with value and stdWrap properties!) but make sure their result is being cast to bool, float or int.
JSON
To build and render a JSON object into your page output.
lib.meta = JSON
lib.meta {
if.isTrue = 1
fields {
title = TEXT
title {
field = seo_title
stdWrap.ifEmpty.cObject = TEXT
stdWrap.ifEmpty.cObject {
field = title
}
}
robots {
fields {
noIndex = BOOL
noIndex.field = no_index
}
}
ogImage = TEXT
ogImage {
dataProcessing {
10 = FriendsOfTYPO3\Headless\DataProcessing\FilesProcessor
10 {
as = media
references.fieldName = og_image
processingConfiguration {
returnFlattenObject = 1
}
}
}
}
}
dataProcessing {
}
stdWrap {
}
}
Copied!
The JSON cObjects comes with a bunch of properties: if, fields, dataProcessing and stdWrap.
Property `if`
Can be used to decide whether or not to render the object.
Property `fields`
Array of cObjects. With special properties per item:
intval/floatval/boolval to cast the result to int, float or bool.
ifEmptyReturnNull to return null in case it's empty
ifEmptyUnsetKey to remove the item in case it's empty
dataProcessing to render data processors (have a look at lib.meta.ogImage for example)
Property `dataProcessing`
This property can be used to render data processors such as MenuProcessor.
Property `stdWrap`
This property can be used to run stdWrap to the already rendered JSON output.
CONTENT_JSON
This cObject basically behaves like TYPO3's CONTENT, the main difference is that content elements are grouped by colPol & encoded into JSON by default.
CONTENT_JSON has the same options as CONTENT but also offers two new options for edge cases in json context.
merge
This option allows to generate another CONTENT_JSON call in one definition & then merge both results into one dataset
(useful for handling slide feature of CONTENT cObject).
For any other plugin, just change the vendorName, extensionName, pluginName and controller options,
and import needed constant and setup values (like for view, persistence and settings in this case).
Then use the constants of that extension to overwrite the paths to the fluid templates:
As last step we need to re-implement the template logic to generate JSON instead of HTML structure.
We do this by creating Fluid templates at the location specified in the previous step.
Because we don't enforce any standard for the JSON structure, we are pretty free here to adjust the
structure to our needs (or to the requests of our frontend developer).
Here is the shortened List.html template which generates news items into a JSON array:
The only difference to make it work with headless is the configuration of the frontend template in TypoScript.
There is an overwritten content object reference in lib.contentElement which we can use, as well as an extended
object with a header definition lib.contentElementWithHeader:
tt_content.demo >
tt_content.demo =< lib.contentElementWithHeader
tt_content.demo {
fields {
content {
fields {
demoField = TEXT
demoField.value = This is a demo content-element
bodytext = TEXT
bodytext {
field = bodytext
parseFunc =< lib.parseFunc_RTE
}
demoSubfields {
fields {
demoSubfield = TEXT
demoSubfield.value = Nested field
}
}
}
}
}
}
Copied!
The definition of fields can be nested until various depth to reflect our desired JSON structure. Also the use of
dataProcessing
is possible the native way like in any other content elements (see content element definitions of this extension).
Create custom TypoScript
To add a default TypoScript object (such as CONTENT) to the fields of your page object you need to make sure to render it a valid JSON.
Here's an example of how you can create a JSON array of multiple objects from a custom DB table:
lib.page {
fields {
related = CONTENT
related {
table = tx_myextension_domain_model_things
select {
pidInList = this
}
renderObj = JSON
renderObj {
fields {
title = TEXT
title.field = title
link = TEXT
link.typolink.parameter.field = uid
link.typolink.returnLast = url
}
# Add recognizable token at the end of this item
stdWrap.wrap = |###BREAK###
}
stdWrap {
# Wrap items into square brackets
innerWrap = [|]# Replace 'inner tokens' by comma, remove others
split {
token = ###BREAK###
cObjNum = 1 |*|2|*| 3
1 {
current = 1
stdWrap.wrap = |
}
2 < .1
2.stdWrap.wrap = ,|
3 < .1
}
}
}
}
}
Copied!
Meta data override
Here's an example of how to override the meta object by data from a DB record:
lib.meta.stdWrap.override.cObject = JSON
lib.meta.stdWrap.override.cObject {
if.isTrue.data = GP:tx_news_pi1|news
dataProcessing.10 = FriendsOfTYPO3\Headless\DataProcessing\DatabaseQueryProcessor
dataProcessing.10 {
table = tx_news_domain_model_news
uidInList.data = GP:tx_news_pi1|news
uidInList.intval = 1
pidInList = 0
max = 1
as = records
fields < lib.meta.fields
fields {
title = TEXT
title.field = title
subtitle = TEXT
subtitle.field = teaser
description = TEXT
description.field = bodytext
}
returnFlattenObject = 1
}
}
Copied!
TypoScript DataProcessors
This extension provides a couple of handy DataProcessors.
You can anytime extend & customize for your needs simply by creating a custom
decorator which implements DefinitionDecoratorInterface or extend the provided
AbstractFormDefinitionDecorator which provides the ability to override the
definition of each element or the whole form definition.
After creating a custom decorator you can attach it to your form simply by setting
formDecorator in the rendering options of the form, see more
It's the EXT:headless equivalent of TYPO3's own DatabaseQueryProcessor.
10 = FriendsOfTYPO3\Headless\DataProcessing\DatabaseQueryProcessor
10 {
table = tt_content
pidInList = 123
as = contents
fields {
header = TEXT
header {
field = header
}
bodytext = TEXT
bodytext {
field = bodytext
parseFunc =< lib.parseFunc_RTE
}
}
}
Copied!
Apart from the properties of TYPO3's DatabaseQueryProcessor (if, table, as and dataProcessing)
it provides the following properties:
fields
overrideFields
returnFlattenObject (Default: 0)
returnFlattenLegacy (Default: 1)
ExtractPropertyProcessor
Extract a single (maybe nested) property from a given array.
Example see below in section on FilesProcessor.
FilesProcessor
lib.meta.fields.ogImage = TEXT
lib.meta.fields.ogImage {
dataProcessing {
# Use the column 'og_image' to render an array with all relevant# information (such as the publicUrl)10 = FriendsOfTYPO3\Headless\DataProcessing\FilesProcessor
10.as = media
10.references.fieldName = og_image
10.processingConfiguration.returnFlattenObject = 1
# Extract only property 'publicUrl' from the above created array20 = FriendsOfTYPO3\Headless\DataProcessing\ExtractPropertyProcessor
20.key = media.publicUrl
20.as = media
}
}
Copied!
FlexFormProcessor
This DataProcessor allows to process a flexform field such as tt_content.pi_flexform
and optionally override its property values.
10 = FriendsOfTYPO3\Headless\DataProcessing\FlexFormProcessor
10 {
fieldName = pi_flexform
as = flexform
overrideFields {
fieldA = TEXT
fieldA {
value = 123
}
}
}
10 = FriendsOfTYPO3\Headless\DataProcessing\RootSitesProcessor
10 {
as = sites
# allow to override provider of data for output processor, if empty defaults to FriendsOfTYPO3\Headless\DataProcessing\RootSiteProcessing\SiteProvider# your-class implementing FriendsOfTYPO3\Headless\DataProcessing\RootSiteProcessing\SiteProviderInterface# example value: Vendor\Project\RootSiteProcessing\CustomSiteProvider
siteProvider =
# allow to override output of processor, if empty defaults to FriendsOfTYPO3\Headless\DataProcessing\RootSiteProcessing\SiteSchema# your-class implementing FriendsOfTYPO3\Headless\DataProcessing\RootSiteProcessing\SiteSchemaInterface# example value: Vendor\Project\RootSiteProcessing\CustomSiteSchema
siteSchema =
# provider configuration, if empty defaults to 'sorting' field from pages table# example value = custom_sorting
sortingField =
# if empty defaults to sort by "sorting" field from `pages` table# your-class implementing FriendsOfTYPO3\Headless\DataProcessing\RootSiteProcessing\SiteSortingInterface# example value: Vendor\Project\RootSiteProcessing\CustomSorting
sortingImplementation =
# list of uid of root pages should be returned, i.e. you have 5 root pages(1,2,3,4,5), but two (4,5) of not ready to display, so you can hide it# example value = 1,2,3
allowedSites =
# automatically fetch root sites from another page/separator and filter sites yaml configs by returned list from database# very useful when you have multi site setup in one instance.# example value = 1
sitesFromPid =
# if empty defaults to uid,title,sorting - list of columns to fetch from database and provided for SiteSchema/DomainSchema to use# example value = uid,title,sorting
dbColumns =
# if empty defaults to "title" field from pages table, get site name from database# example value = your-custom-field-from-pages-table
titleField =
}
Copied!
Image rendering
FileUtility
The file rendering in EXT:headless is handled by FileUtility which renders the following JSON for each file:
properties.byType (0|1): Allows filter file properties by type (i.e. do not return video properties on images)
properties.defaultFieldsByType (coma separated list of fields): Default fields for when enabled option properties.byType
properties.defaultImageFields (coma separated list of fields): Default fields for image type when enabled option properties.byType
properties.defaultVideoFields (coma separated list of fields): Default fields for video type when enabled option properties.byType
properties.includeOnly (string, comma separated): Configure what file properties to return
properties.flatten (0|1): Flatten nested properties (dimensions array) to use with properties.includeOnly
returnFlattenObject: without that flag an array of (multiple) images is rendered. Set this if you're only rendering 1 image and want to reduce nesting.
delayProcessing: can be used to skip processing of images (and have them simply collected with the FilesProcessor), in order to have them processed by the next processor in line (which is generally GalleryProcessor).
fileExtension: can be used to convert the images to any desired format, e.g. webp.
autogenerate:
`retina2x`: set this to render an additional image URI in high quality (200%).
lqip: set this to render an additional image URI with low quality (10%).
* also custom defined size & file formats see example below
10 = FriendsOfTYPO3\Headless\DataProcessing\FilesProcessor
10 {
...
processingConfiguration {
# (1 by default, return new format of file object)
legacyReturn = 0
# Return whole LinkResult object instead simple url
linkResult = 1
# check if we need to conditionally check if we should generate crop variants
conditionalCropVariant = 1
# Generate cacheBusting urls for images and video files
cacheBusting = 1
properties {
# return props by mimeType
byType = 1
# return only properties defined below
includeOnly = alternative,width,height
# you can also alias fields# includeOnly = alternative as alt,width,height# with includeOnly you can use option `flatten` to flatten dimensions array
flatten = 1
}
}
}
Copied!
GalleryProcessor
Configuration
The rendering configuration can be set directly. No processingConfiguration property available!
maxGalleryWidth: set to the core constant {$styles.content.textmedia.maxW}
maxGalleryWidthInText: set to the core constant {$styles.content.textmedia.maxWInText}
columnSpacing: set to the core constant {$styles.content.textmedia.columnSpacing}
borderWidth: set to the core constant {$styles.content.textmedia.borderWidth}
borderPadding: set to the core constant {$styles.content.textmedia.borderPadding}
Using EXT:felogin with the headless extension follows the standard setup as detailed in the felogin documentation.
To test the login functionality without a frontend interface, you can use browser extensions such as RESTer (Firefox) or Insomnia REST Client (Chrome) to simulate form submissions. If the response contains a set-cookie header, the login was successful.
Does EXT:headless work with other extensions?
Yes, the output of virtually any extension can be rendered into the JSON response. For detailed information, refer to the integration of external plugins section of this documentation. Additionally, you can review the code of headless_news as an example of how this integration works.
How to handle redirects in a headless setup?
In a headless setup, redirects should be managed by the frontend application. You can enable and replace core middlewares for handling redirects by setting the headless.redirectMiddlewares feature flag:
Ensure that your frontend application is equipped to handle redirects as specified in the JSON responses.
Can I use custom fields or content elements with EXT:headless?
Yes, EXT:headless supports the customization of JSON responses using TypoScript. You can define custom fields or content elements and extend the JSON output to include these customizations.
For example, to add a custom field, you can modify the TypoScript setup like this:
lib.customField = TEXT
lib.customField.value = My Custom Field
Copied!
This value can then be included in the JSON response as needed.
How to configure language and translation settings?
EXT:headless fully supports TYPO3's language and translation configurations, including fallback settings. To configure languages, follow these steps:
Define your languages in the site configuration YAML file.
Ensure that your content elements and page properties are translated according to TYPO3's multilingual guidelines.
Add the headless flag to your site configuration's YAML file:
headless:true
Copied!
Send the responseElementId field with the ID of the plugin in the body of the plugin data during requests.
For example, a POST request might look like this:
POST https://example.tld/path-to-form-plugin
Content-Type: application/x-www-form-urlencoded
responseElementId=#ELEMENT_ID#&tx_form_formframework[email]=email&tx_form_formframework[name]=test...
Copied!
To handle nested elements, use the responseElementRecursive flag:
POST https://example.tld/path-to-form-plugin
Content-Type: application/x-www-form-urlencoded
responseElementId=#ELEMENT_ID#&responseElementRecursive=1&tx_form_formframework[email]=email&tx_form_formframework[name]=test...
Copied!
What is the JsonView backend module?
The JsonView backend module is an experimental feature that allows previewing the JSON response of a page in different contexts such as pagetype, page arguments, usergroup, and language.
Warning
This is an experimental module. Do not use it in a production environment.
To enable the JsonView backend module, set the headless.jsonViewModule feature flag: