TYPO3 extension for providing access to TYPO3 database table data via a GraphQL
endpoint. Under the bonnet, it uses
graphql-php.
You shold be sufficiently familiar with the basic concepts of GraphQL to use
this extension. For example, you need to know how to create GraphQL schemas. If
you want to learn more about GraphQL or need a refresher, have a look at the
GraphQL docs.
Set up example tables, schemas and configuration with a CLI command.
Introduction
TypoGraph provides a middleware for a GraphQL endpoint. This endpoint accepts
valid GraphQL requests for related data and creates a GraphQL response for the
client.
Note
This extension only supports operations of the type query so far. While it
certainly would be nice to have mutations and subscriptions available, the
technical and security-related challenges are much higher and will require
careful consideration.
Performance Characteristics
TypoGraph implements a DataLoader pattern to prevent N+1 query problems:
Batch Loading: All related records are fetched in a single optimised query per relation type
Request-Scoped Caching: Each record is loaded only once per GraphQL request
Field Selection: Only fields requested in the GraphQL query are fetched from the database
Order Preservation: Related records are returned in the same order as stored (respects MM table sorting)
For example, querying 100 research disciplines with related entries for experts in these disciplines from a research information database results in only two database queries: one query for all taxonomies and one query for all unique disciplines referenced by those taxonomies.
Origins
This extension has been developed by the Digital Academy of the Academy of Sciences and Literature | Mainz while refactoring on the research information system Portal Kleine Fächer, which provides detailed information on minor subjects at German universities and other institutions of higher education. The examples in this documentation still reflect this initial context.
AI Note
The initial basic extension design has been completely done by humans. However, developers of this extension use Claude Code (Sonnet 4.5, 4.6; Opus 4.6) to streamline routine tasks, upgrades, improve code quality etc. All changes depending on AI (as far as we are aware) are confirmed by a qualified human software developer before merged into the main branch.
Once you have configured the TypoGraph extension, you request GraphQL data from your TYPO3 installation via the path
/graphql.
Configuration
TypoGraph is configured via the TYPO3 site configuration file
(config/sites/<site-identifier>/config.yaml), under a top-level typograph:
key.
schemaFiles: Define the locations of the GraphQL schema files to use (more
about schema files below). It makes sense for them to be located somewhere in
the Resources/Private folder of your sitepackage but can really be located
anywhere in your filesystem as long as TYPO3 can access that location.
tableMapping: The TypoGraph extension fetches data for its responses from
your database tables and therefore needs to know which type gets data from which
table. This is configured as <type name>: <table name> pairs.
Here is an example for an application that uses the types Taxonomy,
Discipline and Expert. For better maintainability, the schema is split into
four files, one for the Query schema and one for each type schema (all of this
could also be the content of one single file). These are listed in
schemaFiles. Note that the TypoGraph resolver will concatenate the content of
the schema files in the order they are listed in the configuration.
A GraphQL request will provide one or more type names. The data for these types
can come from any TYPO3 database table, even though you will usually want to
re-use the tables serving your conventional Extbase domain models. For the
TypoGraph resolver to know which table to query for which entity, you need to
provide a list of pairs of entity names and table names, as seen below.
config/sites/<site-identifier>/config.yaml
typograph:# Schema filesschemaFiles:-'EXT:sitepackage/Resources/Private/Schemas/Query.graphql'-'EXT:sitepackage/Resources/Private/Schemas/Taxonomy.graphql'-'EXT:sitepackage/Resources/Private/Schemas/Discipline.graphql'-'EXT:sitepackage/Resources/Private/Schemas/Expert.graphql'# Types to tables mappingtableMapping:taxonomies:tx_myextension_domain_model_taxonomiesdisciplines:tx_myextension_domain_model_disciplinesexperts:tx_myextension_domain_model_experts
Copied!
However, most of the time you will want the option to query relations between
entries from different tables, or data types. For this to happen, you need to
configure the relations between different tables for TypoGraph.
You may also need to post-process certain field values before they are
returned to the client — for example, to resolve TYPO3 link-handling URIs
(t3://...) inside RTE-managed HTML fields. This is done via field value
transforms.
When your GraphQL types reference other types (e.g., a Taxonomy has multiple
Discipline objects), you need to configure how TypoGraph should resolve these
relations from your database.
Why Relations Need Configuration
Unlike scalar fields (strings, integers), object-type fields require the
TypoGraph extension to:
Identify which database column stores the relation reference
Know which table to query for the related records
Understand the storage format (single UID, comma-separated UIDs, or MM table)
Without explicit configuration, TypoGraph will log a warning and return null
for unconfigured relations.
Relation Configuration Structure
Relations are configured under the typograph.relations key in the site
configuration, nested as <TypeName>: <fieldName>: <config>:
config/sites/<site-identifier>/config.yaml
typograph:relations:TypeName:fieldName:sourceField:database_column_nametargetType:RelatedTypeNamestorageType:uid# uid | commaSeparated | mmTable | foreignKey# Additional fields for mmTable storage type:mmTable:tx_some_mm_tablemmSourceField:uid_localmmTargetField:uid_foreignmmSortingField:sorting# Additional fields for foreignKey storage type:foreignKeyField:column_in_target_table
Copied!
Configuration Fields
Field
Required
Default
Description
sourceField
No
Field name in snake_case
Database column in the source table containing the relation reference
(not used for foreignKey type)
targetType
Yes
<none>
GraphQL type name of the related entity (must exist in tableMapping)
storageType
No
uid
How the relation is stored: uid, commaSeparated, mmTable, or
foreignKey
mmTable
For mmTable only
<none>
Name of the MM (many-to-many) intermediary table
mmSourceField
For mmTable only
uid_local
Column in MM table referencing source record
mmTargetField
For mmTable only
uid_foreign
Column in MM table referencing target record
mmSortingField
For mmTable only
sorting
Column in MM table for sorting order
foreignKeyField
For foreignKey only
<none>
Column in target table that references the source record UID
Storage Types
The following variants are available for the storageType field.
1. Single UID (uid)
Use when a database column contains a single UID reference.
Use when the target table has a foreign key column pointing back to the source
record. This handles 'sloppy MM' scenarios where multiple target records can
reference the same source record, potentially with duplicate data. While this
ideally should not happen, sometimes you may have to work with legacy databases
containing denormalized data, inverse relations where child records point to
the parent, or just with cases where somebody couldn't be bothered to set up
proper MM tables (every software project has at least one former team member
like this).
Database structure
tx_taxonomy table:
uid: 5
name: "Applied Sciences"
tx_discipline table:
uid: 1
name: "Physics"
discipline_taxonomy: 5 ← Foreign key pointing to taxonomy
tx_discipline table:
uid: 2
name: "Physics" ← Same name, different record
discipline_taxonomy: 5 ← Same taxonomy reference
tx_discipline table:
uid: 3
name: "Chemistry"
discipline_taxonomy: 5 ← Another discipline for same taxonomy
Copied!
GraphQL schema
type Taxonomy {
name: String
disciplines: [Discipline]
}
type Discipline {
name: String
}
How it works: In this case, TypoGraph queries the target table (tx_discipline) with a WHERE discipline_taxonomy = <source-uid> query and returns all matching records (including duplicates).
Complete Configuration Example
This is a complete example with multiple relation types:
config/sites/<site-identifier>/config.yaml
typograph:# Schema filesschemaFiles:-'EXT:pkf_website/Resources/Private/Schemas/Query.graphql'-'EXT:pkf_website/Resources/Private/Schemas/Taxonomy.graphql'-'EXT:pkf_website/Resources/Private/Schemas/Discipline.graphql'-'EXT:pkf_website/Resources/Private/Schemas/Expert.graphql'# Root elements to tables mappingtableMapping:disciplines:tx_dmdb_domain_model_disciplineexperts:tx_academy_domain_model_personstaxonomies:tx_dmdb_domain_model_discipline_taxonomy# Relation configurationrelations:Taxonomy:# Single UID relationmainDiscipline:sourceField:main_disciplinetargetType:DisciplinestorageType:uid# Comma-separated UIDs relationdisciplines:sourceField:disciplinestargetType:DisciplinestorageType:commaSeparated# Foreign key relation (inverse/legacy MM)relatedDisciplines:targetType:DisciplinestorageType:foreignKeyforeignKeyField:discipline_taxonomyExpert:# MM table relationdisciplines:targetType:DisciplinestorageType:mmTablemmTable:tx_academy_persons_discipline_mmmmSourceField:uid_localmmTargetField:uid_foreignmmSortingField:sorting
Copied!
GraphQL Query Example
With the configuration above, you can now query nested relations:
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>:
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
typolinks
Expands TYPO3 link-handling URIs in HTML href attributes into real
URLs. The following schemes are rewritten:
t3://page?uid=<id>[#<fragment>]
t3://file?uid=<id>
t3://file?identifier=<storage>:<path>
t3://url?url=<target>
t3://record?identifier=<key>&uid=<id>
mailto:<address>
tel:<number>
Any other scheme (plain http/https, in-page anchors, protocol-relative
URLs) is left untouched so that user-pasted external links remain valid.
Resolution uses TYPO3's
TYPO3\\CMS\\Frontend\\Typolink\\LinkFactory, which internally
dispatches to the same link builders (PageLinkBuilder,
FileOrFolderLinkBuilder, EmailLinkBuilder, TelephoneLinkBuilder,
ExternalUrlLinkBuilder, DatabaseRecordLinkBuilder) used during
regular frontend rendering.
With this configuration, a row whose bodytext contains:
<p>See <a href="t3://page?uid=76#64">this page</a> for details.</p>
Copied!
will be delivered to the GraphQL client as:
<p>See <a href="/path/to/page/#c64">this page</a> for details.</p>
Copied!
TSFE-Related Caveats
Because TypoGraph's middleware runs beforetypo3/cms-frontend/page-resolver, there is no running
TypoScriptFrontendController. LinkFactory copes with this by
lazily building a minimal TSFE from the request attributes attached by
the earlier typo3/cms-frontend/site middleware (site, language,
routing). This is sufficient for all built-in link types.
TypoScript-driven link behaviour such as config.linkVars,
config.forceAbsoluteUrls, or custom typolinkBuilder entries is
not applied, because the full TypoScript setup is never loaded. If
your site relies on any of those, configure the equivalent at a
different level (e.g. absolute URLs via Site configuration).
Full lib.parseFunc_RTE processing (paragraph wrapping, <br>
handling, HTML-sanitizer integration, …) is not available from this
middleware position because it depends on the TypoScript setup array
being populated by the regular frontend pipeline. The typolinks
transform deliberately scopes itself to link rewriting for this reason.
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
<?phpdeclare(strict_types=1);
namespaceVendor\Sitepackage\Transformer;
useDigicademy\TypoGraph\Transformer\TransformerInterface;
usePsr\Http\Message\ServerRequestInterface;
finalclassMyCustomTransformerimplementsTransformerInterface{
publicfunctiontransform(mixed $value, ServerRequestInterface $request): mixed{
// return $value unchanged when it is not of a type you handleif (!is_string($value)) {
return $value;
}
// …your post-processing…return $value;
}
}
Copied!
Configuration/Services.yaml (sitepackage)
services:_defaults:autowire:trueautoconfigure:truepublic:falseVendor\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.
When a root query field returns a Connection type (a type whose name ends
with Connection), TypoGraph automatically applies cursor-based pagination. If
the return type is a plain list (e.g. [Discipline]), the resolver behaves as
before and no pagination is applied.
Cursors are opaque, base64-encoded strings. Clients should treat them as opaque
tokens and never parse or construct them manually.
Schema Setup
To enable pagination for a type, you need to define three types in your GraphQL
schema and include the shared Pagination.graphql schema file provided by the
TypoGraph extension.
1. Include the shared PageInfo type
The Pagination.graphql file ships with TypoGraph and defines:
2. Define Connection and Edge types for your entity
Example pagination schema for the Expert type
type Expert {
familyName: String
givenName: String
}
type ExpertConnection {
edges: [ExpertEdge!]!
pageInfo: PageInfo!
totalCount: Int!
}
type ExpertEdge {
cursor: String!
node: Expert!
}
Copied!
3. Define both a plain-list field and a Connection field in the Query type
The recommended convention is to expose two root fields per entity: a plain-list
field for unpaginated access and a *Connection field for paginated access.
This preserves backwards compatibility for clients that do not need pagination.
The first and after arguments on the Connection field are recognised as
pagination arguments and are not turned into WHERE conditions. All other
arguments (like familyName) continue to work as filters on both fields.
Both field names must be present in tableMapping.
Pagination Configuration
Configure default and maximum page sizes in the site configuration:
config/sites/<site-identifier>/config.yaml
typograph:pagination:defaultLimit:20maxLimit:100
Copied!
Setting
Default
Description
defaultLimit
20
Page size when first is not provided
maxLimit
100
Upper bound for first; requests above this are clamped
Querying With Pagination
Using the pagination arguments first and after, or last and before
allows for fine-grained pagination that can be combined the the usual filter
arguments.
TypoGraph supports custom sorting of query results via the
ComparatorInterface. This allows external packages to inject
locale-aware or domain-specific sorting logic without modifying
TypoGraph itself.
How It Works
Sorting is based on two components:
A ComparatorInterface implementation injected via dependency injection.
This defines how strings are compared (e.g. locale-aware collation).
A sortBy argument on the GraphQL query that specifies which field
to sort by.
When both are present, TypoGraph applies the comparator to sort result sets.
When either is absent, results are returned in their default order (UID for
connection queries, database default for plain lists).
Sorting Behaviour
Sorting behaviour differs depending on the query type:
Plain list queries (e.g. [Expert])
The full result set is sorted by the comparator before being returned.
Connection queries (e.g. ExpertConnection)
Cursor-based pagination relies on UID ordering for stable pagination across
pages. The comparator sorts records within the current page only after the
database query and pagination slicing. This means the overall page boundaries
are determined by UID order, but the records within each page are reordered
by the comparator.
Using the sortBy Argument
Clients specify which field to sort by using the sortBy argument on a
query field. To enable this, add sortBy: String to the relevant query
fields in your GraphQL schema:
The sortBy value should be the camelCase GraphQL field name (e.g.
familyName). TypoGraph converts it to the snake_case database column
name automatically.
When sortBy is omitted, no comparator-based sorting is applied and
results are returned in their default database order.
Nonexistent fields: If the sortBy value refers to a field that does
not exist in the result records, sorting is silently skipped and the original
order is preserved.
Implementing a Custom Comparator
TypoGraph ships a ComparatorInterface with a single method:
TypoGraph's ResolverService accepts the comparator as an optional
constructor dependency. When no implementation is registered, the parameter
defaults to null and no custom sorting is applied.
TypoGraph's default resolver dispatches root Query fields by looking them
up in typograph.tableMapping: a root field name is mapped to a
database table, the resolver fetches rows from that table, applies
configured filters and relations, and hands the result to the GraphQL
layer. This works for everything that can be expressed as "list rows
from table X".
Some root fields cannot be expressed that way. Examples include computed
aggregates (e.g., pivoted totals across many joined tables), data
assembled from multiple tables under a single logical query, or any
result whose rows are not a straightforward projection of one table. For
these cases TypoGraph exposes a small extension point, the
CustomResolverInterface, that lets a consuming extension supply a
dedicated resolver for a given root field.
How It Works
Custom resolving depends on two components:
A CustomResolverInterface implementation that handles one
specific root field name. It receives the GraphQL arguments,
ResolveInfo, and (optionally) the current PSR-7 request, and
returns the resolved value in whatever shape the schema expects.
A CustomResolverRegistry that collects every implementation
registered in the DI container via a tagged iterator. Its
get(string $fieldName) returns the handler for a given field, or
null if none is registered.
TypoGraph's ResolverService consults the registry at the start of
every Query-level resolve call. When a custom resolver matches the
current field name, its return value is used directly and the default
tableMapping dispatch is skipped for that field. When no custom
resolver matches, resolution falls through to the regular tableMapping
path and there is no behaviour change for existing fields.
The customResolverRegistry constructor argument of ResolverService
is nullable; when absent (e.g., in projects that do not register any
custom resolver), the feature has zero runtime cost.
When to Use a Custom Resolver
Use a custom resolver when a root field:
Aggregates across multiple tables in a way tableMapping cannot
express (joins, sums, pivots).
Delegates to an existing service whose result shape already matches
the GraphQL type (no reason to duplicate the logic in the schema).
Needs arguments or behaviour that do not fit the standard equality
filters TypoGraph derives from each root field's arguments (e.g.,
enum-valued partitioning parameters).
Prefer plain tableMapping when the field really is "list rows from
one table, optionally filter and paginate". That path is declarative
and gets cursor pagination, sortBy, and field transforms for free.
getFieldName() must return the exact name of the Query root field
this resolver handles. It is used as the lookup key in the registry, so
a field name collision between two resolvers is a configuration error (see the section on collisions below).
resolve() receives the GraphQL arguments (associative array keyed by
the argument name from the schema), the ResolveInfo object
(exposing the selection set and return type), and the current PSR-7
request if one is attached to the resolve call.
The return value must match the schema's declared return type. Once it
is returned, TypoGraph's nested resolution logic walks the structure as
usual, so associative arrays and plain objects with matching properties
both work.
Registering a Resolver
Every CustomResolverInterface implementation is auto-tagged with
typograph.custom_resolver by the _instanceof rule in
EXT:typograph/Configuration/Services.yaml. The CustomResolverRegistry
receives a !tagged_iterator argument populated from that tag.
Important Symfony DI detail: _instanceof is file-local. A rule
declared in one extension's Services.yaml does not apply to
services declared in a different extension's Services.yaml.
Consuming extensions must repeat the rule in their own
Services.yaml so their resolver services pick up the tag:
Configuration/Services.yaml (sitepackage)
services:_defaults:autowire:trueautoconfigure:truepublic:false# Repeat TypoGraph's tagging rule so our CustomResolverInterface# implementations become part of the CustomResolverRegistry's# tagged iterator._instanceof:Digicademy\TypoGraph\CustomResolver\CustomResolverInterface:tags:['typograph.custom_resolver']Vendor\Sitepackage\:resource:'../Classes/*'
Copied!
No explicit service entry for the resolver class is required — the
resource: glob picks it up, and the _instanceof rule tags it.
Registering the Field in the Schema
A custom resolver only dispatches for fields declared in the GraphQL
schema. Add the field to Query.graphql (or wherever your root type
lives) like any other:
Resources/Private/Schemas/Query.graphql
type Query {
# …regular tableMapping-backed fields…
disciplineStats: [DisciplineStat!]!
}
Copied!
Types referenced by a custom-resolved field must be declared in a
schema file loaded before the one that references them. TypoGraph
concatenates the files listed in typograph.schemaFiles in order, so
put the types' schema file earlier in the list than Query.graphql (the Stats schema in this example):
config/sites/<site-identifier>/config.yaml
typograph:schemaFiles:-'EXT:typograph/Resources/Private/Schemas/Pagination.graphql'-'EXT:sitepackage/Resources/Private/Schemas/Stats.graphql'-'EXT:sitepackage/Resources/Private/Schemas/Query.graphql'# …other type files…
Copied!
The root field does not need a tableMapping entry. Fields handled
by a custom resolver never reach the tableMapping dispatch path.
Complete Example
A sitepackage wants to expose a disciplineStats root field that
returns one row per discipline along with the number of experts
attached to it. The count crosses two tables (discipline and
expert) joined by a foreign key, so the default tableMapping
dispatch cannot express it: each DisciplineStat row is a
projection of an aggregate, not of a single database row. A custom
resolver is the right fit.
Resources/Private/Schemas/Stats.graphql
type DisciplineStat {
discipline: String!
expertCount: Int!
}
With the _instanceof rule in the sitepackage's Services.yaml
(shown in the previous section), nothing further is needed. The
resolver is discovered at container build time and dispatched on every
request to disciplineStats.
Dispatch Order and Precedence
At the root level of a Query, ResolverService::resolve() runs in
this order:
Check the CustomResolverRegistry for a handler matching the
current ResolveInfo::$fieldName. If one exists, call it and
return its value.
Otherwise fall through to tableMapping dispatch.
Inside the resolved value (nested fields on returned objects), standard
resolution continues to apply: relation fields declared in
typograph.relations still resolve through the default path, and
field transforms declared in typograph.fieldTransforms still run
against the records returned by your resolver. Your resolver does not
have to re-implement those features; it only has to produce a value
whose shape matches the schema.
Field Name Uniqueness and Collisions
The registry stores at most one resolver per field name. If two
services both implement CustomResolverInterface and return the same
getFieldName() value, the last one registered wins. Symfony DI's
iteration order is deterministic within a single container build but is
not guaranteed across refactors, so colliding field names should be
treated as a configuration error.
If you need to ship multiple alternative resolvers for the same field
(e.g. behind a feature toggle), pick which one to register in your
Services.yaml rather than relying on iteration order.
Error Behaviour
If the current field has no matching custom resolver, dispatch falls
through silently to tableMapping. Fields that are neither
mapped to a table nor handled by a custom resolver return
null — exactly the default behaviour from before the hook was
introduced.
Exceptions thrown inside resolve() are not swallowed by the
registry. They propagate to ResolverService::process() where
GraphQL-level errors are logged and surfaced as a structured error
response. Prefer returning empty arrays or null for
domain-level "no data" conditions so they do not pollute the log.
Consuming projects that never register a custom resolver are not
affected by anything in this document; the registry is injected as
an optional dependency and the hook is a no-op when it is absent.
Error Handling
TypoGraph logs helpful warnings and errors when relations are misconfigured:
Missing configuration: "Relation Taxonomy.disciplines is not configured in the site configuration"
Missing targetType: "Relation Taxonomy.disciplines is missing targetType configuration"
Unmapped target: "Target type Discipline is not mapped to a table in tableMapping"
Missing MM table: "Relation Expert.disciplines with storageType=mmTable is missing mmTable configuration"
Missing foreign key field: "Relation Taxonomy.disciplines with storageType=foreignKey is missing foreignKeyField configuration"
Check your TYPO3 logs if relations return null or empty arrays unexpectedly.
Example Setup
For testing the TypoGraph extension in your system without hooking up any of
your actual data, you can set up some example tables and seed them with a number
of related entries:
Example setup command
vendor/bin/typo3 typograph:example
Copied!
The command accepts two parameters:
--site: If you install the extension in a multisite instance, you can
specify for which site the GraphQL endpoint is configures. Defaults to main if
omitted.
--no-seed: If you only want the example schemas and tables rolled out but
fill them with data yourself, you can skip the entry seeding with this parameter.
The command creates the following tables and (unless you skip the seeding part)
fills them with a handful of entries:
tx_typograph_example_taxonomies
tx_typograph_example_disciplines
tx_typograph_example_experts
tx_typograph_example_experts_disciplines_mm
It will also create the following schema files withing the TypoGraph package
folder:
ResourcesPrivateSchemasExampleQuery.graphql
ResourcesPrivateSchemasExampleTypes.graphql
Finally, the command also adds the necessary configuration entries in
config/sites/<site-name>/config.yaml.
Once the commmand has finished, you need to manually clear the TYPO3 and PHP
caches via the TYPO3 Backend Maintenance Tool. This is recommended because due
to your setup (e.g., if you are working with PHP-FPM), OPcache for worker
threads will not be cleared unless done via the web interface (see, e.g.,
this discussion on TYPO3 cache flushing via the command line).
Now you can query the GraphQL endpoint available at the path /graphql for the
example data. You can also run the Codeception API tests from testsApi
against the endpoint, if needed.
Reference to the headline
Copy and freely share the link
This link target has no permanent anchor assigned.The link below can be used, but is prone to change if the page gets moved.