Skip to content

How-to guideFor front-end developers, module developersDrupal 11

Extending JSON:API

JSON:API mirrors your content model automatically, which is convenient until the raw field names (field_hero_image, body) leak into a public API contract. This guide reshapes the exposed surface, renaming and hiding fields, without changing the entities themselves.

Control the public shape of JSON:API resources: rename fields to a stable public name, hide internal fields, or add computed data. Do this in Drupal so every consumer sees the same contract; do not paper over raw field names in each front end. For read patterns (filtering, includes), see JSON:API basics and relationships and writes first.

Rename or hide fields with the build event

Section titled “Rename or hide fields with the build event”

JSON:API fires a ResourceTypeBuildEvent as it assembles each resource type. An event subscriber can rename a field’s public name or disable it entirely. This subscriber renames the article title field to headline.

web/modules/custom/howto_jsonapi/src/EventSubscriber/ArticleHeadlineSubscriber.php
<?php
declare(strict_types=1);
namespace Drupal\howto_jsonapi\EventSubscriber;
use Drupal\jsonapi\ResourceType\ResourceTypeBuildEvent;
use Drupal\jsonapi\ResourceType\ResourceTypeBuildEvents;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* Reshapes the article JSON:API resource as it is built.
*
* JSON:API mirrors your content model automatically, but you rarely want
* the raw field names on a public API. Subscribe to the resource-type
* build event to rename or hide fields without touching the entity.
*/
final class ArticleHeadlineSubscriber implements EventSubscriberInterface {
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents(): array {
return [ResourceTypeBuildEvents::BUILD => 'onBuild'];
}
/**
* Renames the article title field to "headline" on the public API.
*/
public function onBuild(ResourceTypeBuildEvent $event): void {
if ($event->getResourceTypeName() !== 'node--article') {
return;
}
foreach ($event->getFields() as $field) {
if ($field->getInternalName() === 'title') {
$event->setPublicFieldName($field, 'headline');
}
}
}
}

setPublicFieldName() changes how the field appears in API responses and in filter/sort/include query parameters; disableField() removes it from the API surface completely. The entity, its storage, and the admin UI are untouched.

After installing the module, the renamed field appears in the resource type:

Terminal window
ddev drush pm:install howto_jsonapi
ddev drush php:eval "
\$rt = \Drupal::service('jsonapi.resource_type.repository')->get('node', 'article');
echo \$rt->getPublicName('title');
"

prints headline. A request to /jsonapi/node/article then exposes the title under attributes.headline, and ?sort=headline works. The module’s kernel test asserts the rename and that other bundles keep the original name.

To expose data that is not a stored field, add a computed base field to the entity with hook_entity_base_field_info() and a field class that calculates its value. JSON:API exposes computed fields like any other, so a “reading time” or “full URL” attribute needs no JSON:API-specific code.

When a consumer needs a shape JSON:API cannot express, a controller returning JSON alongside JSON:API is simpler than bending the spec. Reserve it for genuinely custom endpoints.

Was this page helpful?

Drupal is a registered trademark of Dries Buytaert.