AJAX in TYPO3: TypoScript typeNum, Extbase and Middleware

AJAX in TYPO3: TypoScript typeNum, Extbase and Middleware

Modern websites frequently rely on AJAX (Asynchronous JavaScript and XML) to deliver dynamic, responsive user experiences without reloading the entire page. In TYPO3, we can harness the power of AJAX by setting up a dedicated typeNum endpoint in TypoScript and handling asynchronous calls with an Extbase controller. This blog post, a guide to mastering AJAX in TYPO3, will walk you through each step—from configuring TypoScript for AJAX to returning JSON responses and consuming them in your frontend JavaScript.

What is the short answer?

To add AJAX to TYPO3, expose an endpoint that returns JSON and call it with fetch() or XMLHttpRequest. Use a PAGE object with its own typeNum for a quick Extbase endpoint, a PSR-15 middleware for lightweight routes, or an Extbase action that returns jsonResponse(). All three work in TYPO3 v13.4 LTS and v14.

Key takeaways

  • A typeNum creates a separate page type. With disableAllHeaderCode = 1 the response contains no page HTML.
  • Do not set config.no_cache = 1 for the whole page type. The TypoScript reference recommends disabling the cache only for the object that needs it, for example with COA_INT.
  • Extbase actions must return a ResponseInterface. $this->jsonResponse() is the documented shortcut for JSON.
  • A PSR-15 middleware can answer before the page is rendered, but it gives you neither Extbase nor Fluid.
  • Check access inside the endpoint. Since TYPO3 14.2, Extbase offers the #[Authorize] and #[RateLimit] attributes.

Tested with / valid for: TYPO3 v13.4 LTS and v14. Updated: November 2026

Which AJAX method fits my TYPO3 project?

Choose typeNum when you already have an Extbase plugin, a middleware for small standalone endpoints, and an Extbase JSON action when the logic belongs in a controller with repositories.

MethodWorks inBest forCaveat
PAGE with typeNum (TypoScript)v13.4 LTS and v14Reusing an existing Extbase plugin as JSON endpointGoes through frontend rendering. Handle caching per object (COA_INT) instead of config.no_cache.
PSR-15 middlewarev13.4 LTS and v14Small, fast endpoints such as status checksNo Extbase, no Fluid. You do the routing check and input validation yourself.
Extbase action with jsonResponse()v13.4 LTS and v14Business logic with repositories and modelsThe action must return a ResponseInterface.

Sources: PAGE examples, Middlewares, ActionController in the official TYPO3 documentation.

Why Use AJAX in TYPO3?

  1. Improved User Experience: No need to reload the entire page—just update specific components.
  2. Better Performance: Fetch only necessary data, reducing server load and page load times.
  3. Modular and Maintainable: Keep your logic organized by isolating AJAX calls in a dedicated controller/action.

1. Create an AJAX Endpoint with TypoScript

The first step is to create a special PAGE object in TypoScript and assign a unique typeNum to handle your AJAX requests.

ajax_page = PAGE
ajax_page {
    typeNum = 123456  # Unique number for the AJAX call
    config {
        disableAllHeaderCode = 1
        additionalHeaders = Content-type:application/json
        xhtml_cleaning = 0
        admPanel = 0
        debug = 0
        no_cache = 1
    }

    10 = USER
    10 {
        userFunc = TYPO3\CMS\Extbase\Core\Bootstrap->run
        extensionName = YourExtension
        pluginName = YourPlugin
        vendorName = YourVendor
        controller = YourController
        action = yourAjaxAction
    }
}

Key Points

1. typeNum: Assign a unique integer to distinguish this AJAX request from other TYPO3 page types.
2. disableAllHeaderCode: Ensures no extra HTML headers are included—ideal for JSON responses.
3. USER: Routes the request to your Extbase controller/action, which will process the request and return data.

2. Implement the Extbase Controller

Next, create (or update) an Extbase controller action specifically for handling the AJAX request. By leveraging the JsonView, TYPO3 can automatically return a JSON response, making it easy to consume in your JavaScript.

namespace Vendor\YourExtension\Controller;

use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
use TYPO3\CMS\Extbase\Mvc\View\JsonView;

class YourController extends ActionController
{
    protected $defaultViewObjectName = JsonView::class; // Return JSON response

    public function yourAjaxAction()
    {
        // Your business logic here
        $data = [
            'message' => 'Hello, this is a response from the AJAX call',
            'status' => true,
        ];

        // Assign data to the view for JSON output
        $this->view->assign('value', $data);
    }
}

Key Points

1. JsonView: Automatically converts the assigned PHP array/object into JSON output.
2. Business Logic: Perform any server-side operations here—e.g., database queries, validations, or custom business rules.

3. Trigger the AJAX Request from JavaScript

Finally, you need a way to call the newly created AJAX endpoint from your frontend. Below is an example of using plain JavaScript (XMLHttpRequest); however, you can also use fetch() or jQuery’s $.ajax() if preferred.

// Create an XMLHttpRequest object
var xhr = new XMLHttpRequest();

// Define the URL (with the typeNum parameter)
var url = "/?type=123456"; // Replace '123456' with your actual typeNum

// Open the request
xhr.open("POST", url, true);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");

// On successful response
xhr.onload = function() {
    if (xhr.status >= 200 && xhr.status < 300) {
        // Parse JSON response
        var response = JSON.parse(xhr.responseText);
        console.log(response);  // Handle the server response data
    } else {
        console.error('Request failed. Status: ' + xhr.status);
    }
};

// On request error
xhr.onerror = function() {
    console.error('AJAX request failed');
};

// Data to send
var data = "param1=value1&param2=value2";

// Send the request
xhr.send(data);

Key Points

1. xhr.open("POST", url, true): Set the HTTP method (GET or POST), target URL, and asynchronous flag.
2. xhr.setRequestHeader(): (Optional) Use this to set content types or custom headers.
3. Response Handling: JSON responses can be parsed with JSON.parse() for easy access.

Free TYPO3 Guides

Read our free TYPO3 guides for developers and integrators, ready to use in your next project.

Open the free TYPO3 guides

4. (Optional) Configure Clean URLs with Routing

For TYPO3 v9 and above, you can make your AJAX endpoint more user-friendly by setting up route enhancers. In your config.yaml, define a route for your typeNum:

routeEnhancers:
  AjaxType:
    type: Simple
    defaultController: 'Vendor\YourExtension\Controller\YourController::yourAjaxAction'
    requirements:
      type: '\d+'
    default: ''

This lets you craft pretty URLs instead of using /?type=123456. Adjust your JavaScript request accordingly.

Real-World Example

A typical scenario might involve listing records from an extension such as one our TYPO3 extension Helpdesk use:

listAction = PAGE
listAction {
    typeNum = 741852
    config {
        disableAllHeaderCode = 1
        additionalHeaders = Content-type:application/json
        xhtml_cleaning = 0
        admPanel = 0
    }

    10 = USER
    10 < styles.content.get
    10 {
        userFunc = TYPO3\CMS\Extbase\Core\Bootstrap->run
        extensionName = NsHelpdesk
        pluginName = Helpdesk
        vendorName = NITSAN
        controller = Tickets
        switchableControllerActions {
            Tickets {
                1 = list
            }
        }

        # Carry plugin settings along in the AJAX call
        stdWrap.trim = 1
        select {
            where = list_type = "nshelpdesk_helpdesk"
        }
        renderObj < tt_content.list.20.nshelpdesk_helpdesk
    }
}

By referencing /?type=741852 (or your fancy route if configured), you can asynchronously fetch a list of support tickets from Helpdesk and handle them in your JavaScript code.

How do I return JSON from a middleware or an Extbase action?

Both alternatives skip the typeNum page type. A PSR-15 middleware answers early in the request, an Extbase action returns its JSON with jsonResponse().

Option A: PSR-15 middleware

TYPO3 implements PSR-15 middlewares for incoming requests. This example from the TYPO3 documentation returns JSON for the path /check:

 

<?php

declare(strict_types=1);

namespace MyVendor\MyExtension\Middleware;

use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;

class StatusCheckMiddleware implements MiddlewareInterface
{
    public function __construct(
        private readonly ResponseFactoryInterface $responseFactory,
    ) {}

    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        if ($request->getRequestTarget() === '/check') {
            $response = $this->responseFactory->createResponse()
                ->withHeader('Content-Type', 'application/json; charset=utf-8');
            $response->getBody()->write(json_encode(['status' => 'ok']));
            return $response;
        }
        return $handler->handle($request);
    }
}

 

Register it in the frontend stack of EXT:my_extension/Configuration/RequestMiddlewares.php:

 

<?php

return [
    'frontend' => [
        'my-extension/status-check' => [
            'target' => \MyVendor\MyExtension\Middleware\StatusCheckMiddleware::class,
        ],
    ],
];

 

Option B: Extbase action with jsonResponse()

Extbase controller actions must return a ResponseInterface, as the ActionController documentation states. For JSON, return the shortcut $this->jsonResponse() instead of the JsonView from the example above:

 

public function yourAjaxAction(): ResponseInterface
{
    $data = ['message' => 'Hello from the AJAX call', 'status' => true];
    return $this->jsonResponse(json_encode($data));
}

 

Update for the TypoScript example

Since TYPO3 12.3 you can register an Extbase plugin with the EXTBASEPLUGIN content object instead of USER with Bootstrap->run. The old way still works, as the changelog notes:

 

ajax_page.10 = EXTBASEPLUGIN
ajax_page.10.extensionName = YourExtension
ajax_page.10.pluginName = YourPlugin

 

Looking for ready-made functionality instead of writing your own? Browse our TYPO3 extensions. For related reading, see how to build a custom TYPO3 middleware and how to migrate switchable controller actions in Extbase.

How do I secure and cache an AJAX endpoint?

Treat every endpoint as a public URL: validate input, check who may call it, and cache only responses that are safe to share.

  • Use GET for read-only requests and POST for requests that change data, and validate every parameter on the server.
  • Since TYPO3 14.2, #[Authorize] can require a frontend login or user groups for an Extbase action.
  • Since TYPO3 14.2, #[RateLimit] limits requests per IP address. It only works for uncached Extbase actions.
  • Instead of config.no_cache = 1, wrap only the dynamic object in COA_INT, as the TypoScript reference shows.

Common mistakes and troubleshooting

  • HTML around the JSON: set config.disableAllHeaderCode = 1 on the PAGE object.
  • Browser treats the answer as HTML: send the header Content-type:application/json, or use jsonResponse() in Extbase.
  • Endpoint returns the normal page: check that the typeNum is unique and the TypoScript is loaded.
  • Old data after a change: the response was cached, so clear the cache and make the dynamic object uncached.
  • Action fails with an error: make sure the action returns a ResponseInterface.

More questions

What is the difference between typeNum and a middleware?

A typeNum is a page type that TYPO3 renders through the frontend, so it can reuse plugins and TypoScript. A middleware runs earlier in the request and returns a response directly. It is lighter, but you build routing and validation yourself.

Do I need Extbase for AJAX in TYPO3?

No. A PSR-15 middleware can return JSON without Extbase, as the TYPO3 documentation shows. Extbase is useful when you want repositories, models and validation in a controller.

Can I use fetch() instead of XMLHttpRequest?

Yes. fetch() is a browser API and works with any TYPO3 endpoint that returns JSON. Point it at the same URL, for example /?type=123456, and read the answer with response.json().

Is config.no_cache = 1 required for AJAX?

No. Many tutorials set it, but the TypoScript reference advises disabling the cache only for the object that must stay dynamic, for example with COA_INT, and leaving other caches untouched.

Upgrade Playbook for TYPO3

Planning the move to a current TYPO3 version? Follow our step-by-step upgrade playbook.

Read the TYPO3 upgrade playbook

Conclusion

Implementing AJAX in TYPO3 using TypoScript’s typeNum and Extbase is straightforward and powerful. By separating your AJAX logic into a dedicated controller action, you keep the system clean, modular, and easy to maintain. Whether you’re updating small components or building a full-fledged single-page application, TYPO3’s flexibility accommodates it all.

Got questions or unique use cases? Drop them in the comments or reach out to the T3Planet community. We’d love to hear how you’re using AJAX in your TYPO3 projects!

Happy AJAX-ing with TYPO3!

Would you prefer a Ready-made Extension or a Custom one for TYPO3?

Discover ready-made and custom TYPO3 extension solutions from T3Planet Shop, built for performance, scalability, and real project needs.

  • 200+ Developed custom TYPO3 extensions
  • 90+ Score on TYPO3 SEO, speed & accessibility
  • 70+ Published free & premium TYPO3 extensions
  • 15+ Years of TYPO3 experience
  • 5+ Bestseller TYPO3 extensions (T3AI, Slider Revolution etc.)
TYPO3 Extension Gig
Extension