TYPO3 Camp Berlin 2026 · Maik Schneider

TCA rein, JSON-LD raus.

Einfach konfiguriert, schnell ausgeliefert: eine REST-API für jede TCA-Tabelle mit tca_api. Alle Folien zum Durchblättern, die Messwerte stammen aus dem Benchmark in diesem Repository.

Pfeiltasten blättern, F für Vollbild, Esc beendet

TYPO3 Camp Berlin 2026

TCA rein,
JSON-LD raus.

Einfach konfiguriert, schnell ausgeliefert:
eine REST-API für jede TCA-Tabelle mit tca_api

Maik Schneider
Technical Lead TYPO3 · xima

github.com/maikschneider/tca-api

1

GET /_api/articles

Hydra JSON-LD, ein offener Standard

1

@context

Welches Vokabular gilt

2

@id und @type

Adresse und Typ jeder Ressource

3

totalItems und view

Pagination nach Standard

4

Verlinkt

Nur die @id, der Client löst sie auf

5

Eingebettet

Ganzer Datensatz, kein zweiter Request

{
  "@context": "http://www.w3.org/ns/hydra/context.jsonld", ← 1
  "@type": "hydra:Collection",
  "hydra:totalItems": 10000, ← 3
  "hydra:member": [{
    "@id": "/_api/articles/4", ← 2
    "@type": "Article",
    "title": "Caching in TYPO3",
    "reading_time": 12,
    "author": "/_api/authors/7", ← 4
    "tags": [{"@id": "/_api/tags/1", "@type": "Tag", ← 5
               "title": "PHP"}, …]
  }, … 19 weitere],
  "hydra:view": { ← 3
    "@type": "hydra:PartialCollectionView",
    "hydra:first": "/_api/articles?page=1",
    "hydra:next": "/_api/articles?page=2",
    "hydra:last": "/_api/articles?page=500"
  }
}

2

Dieselbe Aufgabe, sechs APIs

GET /_api/articles?page=1&itemsPerPage=100

100 Artikel

aus 10.000, mit Gesamtzahl und Pagination

4 Felder

title, teaser, published, reading_time

3 Relationen eingebettet

1 Autor, 2–4 Tags, 1–3 Kategorien je Artikel

Tiefe 1

eingebettete Datensätze ohne weitere Relationen

APIZeilen Config/CodeQueriesApp-Zeit
tca_api30515,7 ms
sg_apicore¹2721649,0 ms
t3api184304412,4 ms
Record API, von Hand201305154,4 ms
nnrestapi221307673,4 ms
Extbase, von Hand281307148,6 ms

¹ ohne Kategorien: sg_apicore kann sie nicht einbetten, die Response enthält also eine Relation weniger.

Kalter Cache · TYPO3 14.3.8 · Production-Modus · k6, 8 virtuelle User · Median aus 3 Läufen 

3

Warum noch eine API-Extension?

Wie es zu tca_api kam

01

Statische Seiten

Static File Cache ist extrem performant, also kein USER_INT

02

Web Components

Dynamische Daten-Elemente überall platzieren

03

APIs

Liefern die dynamischen Daten, erste Wahl: t3api

04

Kalter Cache

Häufige Änderungen, im Extremfall tausende Datensätze im Client: Serialisierung wird zur Grenze

05

tca_api

So einfach wie t3api, ohne Limit bei der Performance

Das Ziel: einfach konfigurieren und schnell ausliefern, beides zugleich.

4

06 · Beispiel Personenverzeichnis

Der Cache ist ständig kalt

Backend: neue Person, Austritt

Frontend-Update: Profil gepflegt

Deployment

07:00

10:00

13:00

16:00

19:00

Jeder Strich leert den Cache. Der nächste Request ist kalt.

Der Engpass bei t3api:

die Serialisierung

5

Die Extension

tca_api: frei verfügbar, in drei Schritten startklar

TER-Eintrag TCA API, tca_api / stable: REST API based on TYPO3 TCA, exposes database tables as Hydra JSON-LD resources. Unterstützt TYPO3 13 LTS und 14 LTS, Tags Api, REST, tca, hydra, headless, json-ld

GitHub

github.com/maikschneider/tca-api

TER

extensions.typo3.org/extension/tca_api

Doku

docs.typo3.org/p/maikschneider/tca-api

Version 2.2.0 · TYPO3 13.4 und 14.3 · PHP 8.2+ · GPL-2.0-or-later

1

Installieren

composer require \
  maikschneider/tca-api

2

Site Set hinzufügen

dependencies:
  - maikschneider/tca-api

3

Ressource anlegen

Configuration/TcaApi/Articles.php
→ GET /_api/articles

6

Schritt 3 · Ressource anlegen

Das ist eine komplette Ressource

// Configuration/TcaApi/Articles.php
return [
    'general' => [
        'table' => 'tx_bench_article',
        'resourceName' => 'articles',
        'resourceType' => 'Article',
    ],
];

Man bekommt: Liste, Detail, Pagination, Hydra JSON-LD, OpenAPI und Swagger UI.

Man spart sich: Controller, Extbase-Models, Routen, generierte Klassen.

Felder und Typen kommen aus dem TCA. Nichts läuft auseinander.

7

Und im Backend?

Konfiguration und Doku, ohne eine Zeile Code mehr

TYPO3-Backend, System › Configuration: Baum der TCA API Configuration mit Ressourcen categories, customers, orders, pets, tags; Suche nach table ergibt 13 Treffer

1

System › Configuration

Die fertige Konfiguration aller Ressourcen als durchsuchbarer Baum

TYPO3-Backend, Administration › Integrations › TCA API: Swagger UI der Petstore API mit OpenAPI 3.1 und GET, POST, PUT, PATCH, DELETE für /_api/categories

2

Administration › Integrations

Swagger UI mit OpenAPI 3.1, zum Ausprobieren oder als openapi.json

8

Highlights

Die häufigsten Fälle, jeweils ein paar Zeilen Konfiguration

01

Relationen einbetten

Autor und Tags im selben Request

02

Filtern und Suchen

auch über Relationen hinweg

03

Responsive Bilder

alle Crop Variants, fertig verarbeitet

04

Links und Detail-URLs

ohne TypoScript

05

Formulare und Uploads

Schreiben mit Datei in einem Request

Ohne Konfiguration dabei: Mehrsprachigkeit, Pagination, OpenAPI und Swagger UI.

9

Anwendungsfall 1 · Relationen

Artikel mit Autor und Tags in einem Request

Konfiguration · Articles.php

'columns' => [
  'author' => ['embed' => true],
  'tags' => ['embed' => true],
],

Ergebnis

GET /_api/articles
{
  "title": "Caching in TYPO3",
  "author": {
    "@id": "/_api/authors/7",
    "name": "Ada Lovelace"
  },
  "tags": [{"title": "PHP"}, {"title": "Cache"}]
}

Eine Query pro Relation, egal ob 20 oder 1.000 Datensätze.

Anderswo: Model pro Tabelle, Mapping, Serializer-Gruppen und oft eine Query pro Datensatz.

10

Anwendungsfall 2 · Filter und Suche

Filtern und Suchen, auch über Relationen

Konfiguration · Articles.php

'filters' => [
  'categories.title' => ExactFilter::class,
  'q' => [SearchFilter::class, [
    'columns' => ['title', 'author.name'],
  ]],
],

Ergebnis

GET /_api/articles
?filters[categories.title]=Technik
&filters[q]=hopper

→ nur Artikel aus „Technik“,
  deren Titel oder Autor „hopper“ enthält

Ein Punkt im Namen genügt, um über Relationen zu filtern.

Anderswo: pro Filter eine eigene Repository-Methode mit Joins.

11

Anwendungsfall 3 · Bilder

Responsive Bilder mit allen Crop Variants

Konfiguration · Articles.php

'columns' => [
  'image' => ['image' => [
    'maxWidth' => 1200,
    'fileExtension' => 'webp',
  ]],
],

Ergebnis

"image": {
  "publicUrl": "/fileadmin/hero.jpg",
  "metadata": {"alternative": "…"},
  "cropVariants": {
    "desktop": {"publicUrl": "…/hero_d.webp"},
    "mobile": {"publicUrl": "…/hero_m.webp"}
  }
}

Die Crops aus dem Backend kommen fertig verarbeitet an.

Anderswo: pro Variante ein Getter mit eigener Bildverarbeitung.

12
13

Anwendungsfall 5 · Formulare

Datensatz anlegen, mit Datei, in einem Request

Konfiguration · Articles.php

'general' => [
  …
  'operations' => ['list', 'show', 'create'],
  'storagePid' => 2,
],
'security' => ['create' => AccessRole::FE_USER],
'columns' => [
  'image' => ['upload' => [
    'folder' => '1:/user_upload/',
  ]],
],

Ergebnis

curl -X POST /_api/articles \
  -F "title=Neuer Artikel" \
  -F "image=@foto.jpg"

→ 201 Created
→ 422 mit Feldfehlern, Regeln aus dem TCA

Gespeichert wird über den DataHandler: Hooks, Historie und Cache-Leerung inklusive.

Anderswo: Controller, Validatoren, Datei-Handling und oft ein zweiter Request für den Upload.

14

Live-Demo

15

Performance

Zwei Zustände, die zählen

Warmer Cache

Die Antwort liegt fertig bereit. Hier ist fast jede API schnell.

Der Normalfall zwischen zwei Änderungen

Kalter Cache

Der erste Request nach jeder Änderung. Jetzt zählen Queries und Serialisierung.

Im Personenverzeichnis: ständig

So wurde gemessen

6 APIs, dieselbe Aufgabe, jede im eigenen Paket mit eigenem Cache · TYPO3 14.3.8, Production-Modus · k6 mit 8 virtuellen Usern · Median aus 3 Läufen · Code und Rohdaten öffentlich

16

Warmer Cache · 100 Datensätze

Mit warmem Cache sind alle schnell

Record API, von Hand

14 ms

t3api

21 ms

Extbase, von Hand

21 ms

tca_api

35 ms

sg_apicore¹

36 ms

nnrestapi

90 ms

Warm entscheidet der Cache, nicht die API. Die Record API mit eigenem Antwort-Cache ist hier vorn.

Antwortzeit p50 · TYPO3 14.3.8, Production-Modus · k6, 8 virtuelle User · Median aus 3 Läufen · ¹ ohne Kategorien

17

Kalter Cache · 100 Datensätze

Kalt trennt sich das Feld

tca_api

57 ms

sg_apicore¹

107 ms

Record API, von Hand

216 ms

Extbase, von Hand

248 ms

t3api

481 ms

nnrestapi

716 ms

Ohne Cache zählt, wie viele Queries eine API absetzt: tca_api 5, die anderen 216 bis 307.

Antwortzeit p50 · TYPO3 14.3.8, Production-Modus · k6, 8 virtuelle User · Median aus 3 Läufen · ¹ ohne Kategorien, eine Relation weniger

18

Und warum ist das schnell?

Drei Entscheidungen gegen den üblichen TYPO3-Weg

01

Konfiguration statt Code

Ein PHP-Array pro Ressource. Keine Controller, Models oder generierten Klassen.

my_ext/
└── Configuration/
    └── TcaApi/
        ├── Articles.php ← Ressource
        ├── People.php
        └── Overrides/
            └── Extend.php ← erweitern

Erweitern wie im TCA: Overrides/

02

Arrays statt Objekte

Von der Datenbankzeile direkt zu JSON-LD.

TCA

Spalten, Typen, Relationen

↓

QueryBuilder-Zeilen

einfache Arrays

↓

Nach TCA-Typ normalisiert

Datum, Bilder, Links, Relationen

↓

Hydra JSON-LD

kompatibel mit API Platform

Kein DataMapper, keine Hydration, keine Proxies

03

Preloading statt Lazy Loading

Eine Query pro Relation, nie eine pro Zeile.

Cache-Lookup

Treffer: 0 Queries

↓

Basis-Query

Filter, Sortierung, Seite, Count

↓

EmbedPreloader

ein WHERE uid IN (…) pro Relation

↓

JSON-LD

mit Cache-Tags gespeichert

Invalidierung über den DataHandler

19

Mehr Datensätze, dieselben fünf Queries

━ tca_api   ━ Extbase, von Hand (Record API, t3api, nnrestapi verlaufen gleich)   · Queries pro Request, kalter Cache, log. Skala

10k

1.000

100

10

1

1

5

20

50

100

500

1.000 Datensätze

5

3.007

20

Kalter Cache · 1.000 Datensätze

Bei großen Datenmengen: Faktor 10 und mehr

tca_api

140 ms

Record API, von Hand

1.524 ms

Extbase, von Hand

1.782 ms

nnrestapi

4.685 ms

t3api

6.045 ms

Genau der Fall aus dem Personenverzeichnis: alle Daten auf einmal zum Client.

Antwortzeit p50 · TYPO3 14.3.8, Production-Modus · k6, 8 virtuelle User · Median aus 3 Läufen · sg_apicore fehlt: höchstens 100 Datensätze pro Seite

21

Was Integratoren selbst schreiben

sg_apicore

27 Zeilen · 0 Klassen

tca_api

30 Zeilen · 0 Klassen

t3api

184 Zeilen · 4 Klassen

Record API

201 Zeilen · 3 Klassen

nnrestapi

221 Zeilen · 7 Klassen

Extbase

281 Zeilen · 6 Klassen

Gezählt pro Benchmark-Paket: PHP-, YAML- und Config-Dateien für dieselbe Artikel-Aufgabe, ohne Site-Set-Dependency oder Routen-Import.

22

TCA rein, JSON-LD raus.

Fragen?

Extension

github.com/maikschneider/tca-api

Benchmark

github.com/maikschneider/typo3-api-benchmark

Kontakt

Maik Schneider

23

← → · F · Esc