Architecture Note: Using TYPO3 Content Repository (GrDB) for Decoupled Content
Learn how TYPO3's GrDB API enforces data boundaries, enables decoupled object‑oriented content, and what checks keep it performant.
11 Jul 2026, 09:33 UTC

Requirements for Decoupled Content
To use the TYPO3 Content Repository (GrDB) API you must model content as independent objects rather than rows tied to a page. This means:
- Define each entity (e.g., Product, Tag) with its own properties and relationships.
- Map relationships explicitly in the TYPO3 configuration (typically via
TCAorext_tables.php). - Route all data access through a Repository service instead of direct SQL or global helpers.
The Smallest Suitable Design: Repository Mediator
The minimal working design introduces a Repository that acts as a mediator between the persistence layer and domain logic. The Repository receives a QueryConfiguration, builds a query, hydrates the result into PHP objects, and returns them.
Example: fetching a Product with its associated Tags.
// Pseudo‑code inside a Repository class
public function findProductWithTags(int $productId): ?Product
{
$config = new QueryConfiguration();
$config->setAllowedFields(['title', 'description', 'sku']);
$config->include('tags'); // eager‑load the relation
return $this->repository->matching(
$this->queryBuilder()->eq('uid', $productId)->setConfiguration($config)
)->getFirst();
}
This snippet shows how the Repository hides the SQL join and returns a fully hydrated Product object whose getTags() method returns a collection of Tag objects.
Trust and Data Boundaries
Data leakage is prevented by the QueryConfiguration object, which acts as a filter defining exactly which properties are serialized for the frontend.
$config = new QueryConfiguration();
$config->setAllowedFields(['title', 'description', 'price']);
$config->excludeFields(['internal_notes', 'db_uid']);
Only the fields listed in setAllowedFields (or not excluded) will appear in the API response, even if the underlying table contains additional columns.
Operational Checks and Performance
Because GrDB relies on object hydration, performance hinges on avoiding N+1 query patterns and monitoring cache usage.
- Eager loading: Use the
includeparameter (or equivalent) to fetch related objects in a single database round‑trip. - Cache monitoring: Enable the TYPO3 Profiler and watch the number of database queries per request; a steady increase may indicate missing
includeclauses. - Schema synchronization: Verify that every custom field declared in TCA exists in the database table; a mismatch throws a mapping exception during hydration.
Failure Modes and Design Triggers
The most common failure is partial data loading when a referenced record is removed outside TYPO3 (e.g., via a direct database delete). The API getters return null for the missing relation, which the frontend must handle.
Conditions that would prompt a redesign include:
- Frequent need for deep nesting (>3 levels) that forces many joins; consider flattening data or using a dedicated search index.
- Strict real‑time consistency requirements where the caching layer cannot tolerate stale objects; you may bypass the Repository and query the persistence layer directly.
- Regulatory constraints that demand explicit audit trails on every field; the generic QueryConfiguration filter may be insufficient, requiring a custom serialization layer.
To check whether your current setup respects data boundaries, create a test ContentElement that adds a hidden property (e.g., internal_flag) to the table, configure the Repository to exclude it, and then request the element via the frontend API; the response should not contain internal_flag. Observing its absence confirms the boundary works.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.