Beyond HTML delivery
An Extbase plugin usually renders HTML into a page. Sooner or later an extension might need something else: JSON for a script that updates part of the page, XML for a feed, an HTML fragment that replaces a list without reloading the page, or data for a frontend built with a separate JavaScript framework.
TYPO3 offers several ways to deliver such output. They differ in what they have available when they run: the site and language, the frontend user information, the TypoScript configuration, the page cache, and the Extbase plugin machinery itself. Choosing between them is mostly a question of which of these the output actually needs.
Ways to deliver non-HTML output from TYPO3
| Option | What it has available | Suited for |
|---|---|---|
| The regular page | Everything a page has, including the page cache | Reloading part of a page |
| A typeNum page type | Everything a page has, including the page cache | Feeds, sitemaps, exports, parts of a page loaded by a script |
| A plugin action returning non-HTML on a regular page | Everything a page has | Custom: yours to decide |
| throwStatus() in an Extbase action | Everything a page has, up to the action | Error responses |
| A PSR-15 middleware | Depends on its position in the middleware chain; no page cache, no Extbase controller | Small data endpoints |
| An eID script | Site; no frontend user, page or TypoScript | Maintaining existing code |
| A backend AJAX route | Backend user and backend context | Backend modules only |
| A headless setup | Depends on the solution | A frontend built separately from TYPO3 |
Requesting the regular page and using part of it
A script can request the regular page URL, for example with fetch(), and take the element it needs out of the returned HTML. A filter or a pagination that reloads a list without reloading the page works this way. Nothing has to be configured: the plugin, its content element, its links and the page cache behave exactly as they do for a visitor. The cost is that the whole page is transferred, which is cheap when it comes from the page cache. Because the page works without the script as well, this also suits progressive enhancement.
Rendering the plugin in a typeNum page type
A dedicated TypoScript
PAGE
object with its own
type renders the plugin instead of the page template. It is
the most widespread approach. The output is another representation of the
page, with its site, language, access checks and page cache. Rendering the
plugin's content element keeps the settings an editor chose, so a list page
can offer its own selection as a feed. A page type also delivers parts of a
page that a script loads, such as the next page of a list.
See also
Delivering Extbase output with a typeNum page type — how it works, what it costs, and when it is the right tool.
Returning non-HTML from a plugin action on a regular page
An action of a plugin placed on a regular page can return
$this->json. Extbase does not replace the page with it. The
JSON is inserted into the page HTML like any other plugin output, and the
Content- header of the action's response becomes the content type
of the whole page. The visitor receives an HTML page that declares itself as
JSON. This is a common mistake rather than an option, see
the pitfall An action returning JSON breaks the page.
Proceeding that way or with another output format might be a suitable course
of action, but the handling of how the frontend copes is fully up to the
developer.
Ending the request from an Extbase action with throwStatus()
Action stops the rendering of the page and
sends a response with the given status code and content instead. It is meant
for an action that has to answer with an error, such as 404 Not Found or
403 Forbidden. It accepts content, which makes it look like a way to send
data. It sets no content type, though, and the exception it throws is
internal to TYPO3, so it is not an endpoint for data.
Answering in a PSR-15 middleware
A middleware can answer a request before any page is rendered. For a small endpoint that returns a few values, it is the lightest option: no page rendering, no TypoScript evaluation, and a response that is entirely under the extension's control.
What a middleware has available depends on where it is placed in the chain. After the site has been resolved, the site and language are available as request attributes; after frontend user authentication, the frontend user is. No Extbase controller is dispatched there, and repositories need their configuration established first, see Extbase in middlewares.
Using an eID script
A request with the query parameter e is dispatched to the target
registered for that name in
$GLOBALS['TYPO3_CONF_VARS']['FE']['eID_include'].
The core uses this itself, for example to
deliver files of non-public storages.
eID is not deprecated, but it is not a choice for new code. It is handled by a middleware that the core marks as internal and as possibly removed in the future. In the current order of the middleware chain it runs after the site has been resolved, although nothing in its registration guarantees that, and before maintenance mode, frontend user authentication, page resolution, and TypoScript. An eID script therefore has no frontend user and no TypoScript, and it answers even while the site is in maintenance mode. A middleware of the extension's own, placed where it has what it needs, does the same job without these limitations.
Backend AJAX routes are for the backend only
Backend AJAX routes answer requests from backend modules. They require a logged-in backend user and run in the backend context, so they are not available to the frontend, however convenient their registration looks.
Delivering content to a headless frontend
In a headless or decoupled setup, a frontend built separately, for example with a JavaScript framework such as Next.js or Nuxt, requests content from TYPO3 as JSON and renders it itself. The community extension friendsoftypo3/headless outputs TYPO3 content in JSON format for such setups. Extbase can supply the data, while Fluid templates have no role in the output.