Resource Definition
Resources are defined as PHP files placed in any active extension's
Configuration/ directory. No manual registration is needed —
the extension auto-discovers all *. files from every active package's
Configuration/ directory at boot time and caches the result.
Each file returns a PHP array with the resource configuration.
To modify a resource defined by a third-party package, place an override file
in Configuration/ — see Overriding Third-Party Resource Configs.
General section
The general key defines the basic resource properties:
return [
'general' => [
'table' => 'tx_myext_domain_model_article',
'resourceName' => 'articles',
'resourceType' => 'Article',
// operations defaults to ['list', 'show'] — omit for read-only resources
'itemsPerPage' => 20,
'storagePid' => 1,
],
];
| Key | Description |
|---|---|
table | TYPO3 database table name. |
resourceName | URL slug used in /_api/{resourceName}. |
resourceType | JSON-LD @type value. |
type | Set to 'userinfo' to create a userinfo endpoint. |
operations | Array of enabled operations: list, show, create, update,
delete. Defaults to ['list', 'show'] when omitted. |
itemsPerPage | Default page size for list operations (overrides the global site setting). |
maxItemsPerPage | Upper limit for itemsPerPage; when set, the requested page size is
clamped to this value. No limit when omitted. |
storagePid | Page ID for newly created records. Also constrains reads to this page
unless readStoragePids is set. When omitted, reads are not
pid-constrained and writes target page 0. |
readStoragePids | Widens the read-side pid constraint independently of storagePid.
Accepts an array ([42, 17, 5]) or comma-separated list
('42,17,5') — reads then use pid IN (...) — or the sentinel
'*' to read from all pages regardless of the write target. When
omitted, reads fall back to storagePid. This is the complete read
set: list the write page explicitly if it should also be readable. |
writeMode | Write execution strategy. acting_user (default) — DataHandler runs
under the authenticated user identity, preserving the actor for audit
purposes. system_admin — uses a synthetic backend-admin context,
bypassing all TYPO3 ACL boundaries. Only enable for fully trusted,
internal-only APIs. |
group | OpenAPI tag grouping this resource's operations in Swagger UI. A string
('Editorial') or an array with a name and optional
description. Defaults to resourceType — see
Endpoint grouping (OpenAPI tags). |
Write mode
By default, write operations (create, update, delete) run under the
acting-user context — TYPO3's DataHandler preserves the caller's identity
for audit purposes. Set writeMode to system_admin to bypass TYPO3 ACL
checks entirely. Only use this for trusted, back-channel APIs.
'general' => [
'table' => 'tx_myext_domain_model_article',
'resourceName' => 'articles',
'resourceType' => 'Article',
'operations' => ['create', 'update', 'delete'],
'writeMode' => 'system_admin', // default: 'acting_user'
],
Warning
system_admin mode bypasses all TYPO3 access-control restrictions on
writes. Never use it for public-facing endpoints.
Storage pages (read vs. write)
storagePid is the single write target — newly created records land
there. By default it also constrains reads to that one page. To read from
several pages while still writing into one, add readStoragePids:
'general' => [
'table' => 'tx_myext_domain_model_article',
'resourceName' => 'articles',
'resourceType' => 'Article',
'operations' => ['list', 'show', 'create'],
'storagePid' => 42, // writes go here
'readStoragePids' => [42, 17, 5], // reads span these pages
],
Use the sentinel '*' to read from every page regardless of the write
target:
'storagePid' => 42, // writes go here
'readStoragePids' => '*', // reads ignore pid entirely
readStoragePids is the complete read set — it does not implicitly
include storagePid. List the write page explicitly if it should be
readable too.
Endpoint grouping (OpenAPI tags)
By default each resource gets its own Swagger UI section, named after its
resourceType. Set group to place several resources under a shared,
named section:
'general' => [
'table' => 'tx_myext_domain_model_article',
'resourceName' => 'articles',
'resourceType' => 'Article',
'group' => 'Editorial',
],
Use the array form to add a description shown above the section:
'group' => [
'name' => 'Editorial',
'description' => 'Editorial content endpoints',
],
Point several resources at the same name to merge them into one section.
Sections appear in resource-registration order; the first configured
description for a group wins. Omitting group falls back to
resourceType, so grouping is fully opt-in and backward compatible.
Minimal example (zero-config)
Three keys are all that is needed for a public read-only resource. operations
defaults to ['list', 'show'] and both are PUBLIC by default:
return [
'general' => [
'table' => 'tx_myext_domain_model_article',
'resourceName' => 'articles',
'resourceType' => 'Article',
],
];
To enable write operations, add them explicitly and configure security:
use MaikSchneider\TcaApi\Enum\AccessRole;
return [
'general' => [
'table' => 'tx_myext_domain_model_article',
'resourceName' => 'articles',
'resourceType' => 'Article',
'operations' => ['list', 'show', 'create', 'update', 'delete'],
'storagePid' => 1,
],
'security' => [
'create' => AccessRole::FE_USER,
'update' => AccessRole::FE_USER,
'delete' => AccessRole::BE_ADMIN,
],
];
Full example
use MaikSchneider\TcaApi\Enum\AccessRole;
use MaikSchneider\TcaApi\Filter\ExactFilter;
use MaikSchneider\TcaApi\Filter\MmFilter;
use MaikSchneider\TcaApi\Serializer\FileProcessing\FileProcessor;
use MaikSchneider\TcaApi\Serializer\Processing\TypoLinkProcessor;
return [
'general' => [
'table' => 'tx_myext_domain_model_article',
'resourceName' => 'articles',
'resourceType' => 'Article',
'operations' => ['list', 'show', 'create', 'update', 'delete'],
],
'columns' => [
'title' => [
'type' => 'string',
'groups' => ['list', 'show', 'create', 'update'],
'required' => true,
'validators' => [
['type' => 'maxLength', 'max' => 20],
['type' => 'minLength', 'min' => 3],
['type' => 'regex', 'pattern' => '/^[\w\s]+$/u'],
],
],
'color_id' => ['groups' => ['list', 'show', 'create', 'update']],
'categories' => ['groups' => ['list', 'show', 'create', 'update']],
'profile_photo' => ['groups' => ['list', 'show']],
'downloads' => [
'groups' => ['list', 'show'],
'processor' => FileProcessor::class,
],
'article_url' => [
'groups' => ['list', 'show'],
'processor' => TypoLinkProcessor::class,
],
],
'filters' => [
'title' => ExactFilter::class,
'color_id' => ExactFilter::class,
'categories' => MmFilter::class,
],
'order' => [
'allowed' => ['title', 'uid'],
'default' => ['uid' => 'asc'],
],
'security' => [
'list' => AccessRole::PUBLIC,
'show' => AccessRole::PUBLIC,
'create' => AccessRole::FE_USER,
'update' => AccessRole::FE_USER,
'delete' => AccessRole::BE_ADMIN,
],
];