Field Value Transforms 

Field value transforms run on raw database values after they are fetched and before they are handed to the GraphQL layer. They allow site-specific post-processing of individual fields without leaking the processing logic into the schema or into client code.

The primary use case is resolving TYPO3 link-handling URIs (t3://page, t3://file, t3://url, mailto:, tel:) that RTE-managed fields such as tt_content.bodytext store verbatim in the database. In a normal Fluid render, lib.parseFunc_RTE resolves these to real URLs during page rendering. TypoGraph bypasses the TSFE-driven rendering pipeline, so without a transform the raw t3:// URIs would reach the client as-is.

Configuration Structure 

Transforms are configured under the typograph.fieldTransforms key in the site configuration, nested as <TypeName>: <fieldName>: <transformName>:

config/sites/<site-identifier>/config.yaml
typograph:
  fieldTransforms:
    TypeName:
      fieldName: transformName
Copied!

Keys follow GraphQL naming: TypeName matches a type in your schema (e.g. Content) and fieldName matches a field on that type (e.g. bodytext). Internally the resolver converts the field name to the corresponding database column name via camelCaseToLowerCaseUnderscored, the same conversion used everywhere else in TypoGraph.

Built-In Transforms 

Registering Custom Transforms 

To add your own transform, implement Digicademy\\TypoGraph\\Transformer\\TransformerInterface and register it under a short name in Configuration/Services.yaml:

Classes/Transformer/MyCustomTransformer.php
<?php

declare(strict_types=1);

namespace Vendor\Sitepackage\Transformer;

use Digicademy\TypoGraph\Transformer\TransformerInterface;
use Psr\Http\Message\ServerRequestInterface;

final class MyCustomTransformer implements TransformerInterface
{
    public function transform(mixed $value, ServerRequestInterface $request): mixed
    {
        // return $value unchanged when it is not of a type you handle
        if (!is_string($value)) {
            return $value;
        }
        // …your post-processing…
        return $value;
    }
}
Copied!
Configuration/Services.yaml (sitepackage)
services:
  _defaults:
    autowire: true
    autoconfigure: true
    public: false

  Vendor\Sitepackage\:
    resource: '../Classes/*'

  # Extend the TransformerRegistry provided by TypoGraph with the
  # new transform under a short name. Keep the `typolinks` entry so
  # the built-in transform remains available alongside yours.
  Digicademy\TypoGraph\Transformer\TransformerRegistry:
    arguments:
      $transformers:
        typolinks: '@Digicademy\TypoGraph\Transformer\TypolinksTransformer'
        myCustom: '@Vendor\Sitepackage\Transformer\MyCustomTransformer'
Copied!

Transforms must:

  • Return the input unchanged for value types they do not handle.
  • Avoid throwing for unexpected inputs. The resolver logs and swallows exceptions so that one misbehaving transform does not abort the entire GraphQL response, but logging contributes noise; prefer a defensive early return.

Error Behaviour 

  • If a configured transform name has no entry in the registry, the resolver logs a warning and leaves the raw value in place. This is usually a typo in site configuration or a missing DI wiring.
  • If a transform throws, the resolver logs an error and leaves the raw value in place for the affected record.
  • If the current request is not attached (only possible when calling ResolverService::process() directly without a request), transforms are skipped entirely.