If you’re looking for a way to provide a custom collections like
In this article, I will not explain how to create a custom REST Resources.
jsonapi/articles/featured or contextual data like a jsonapi/me. You’re in the good place.In this article, I will not explain how to create a custom REST Resources.
If you’re not familiar with JSON:API, I recommend you watch the following short video. It will provide valuable context for the remainder of this blog post:
- A 5-minute demo of Drupal’s JSON:API implementation.
Here I will try to expose you a step-by-step guide which explains why and how you can create a custom JSON:API Resource that leverage JSON:API Core feature.
JSON:API in short
To be franc - I don't want to read the story. Bring me to the code.
The JSON:API module is becoming wildly popular as an out-of-the-box way to provide an API server.
Why? Because it implements the {json:api} specification.
It’s still a RESTful interface, but the specification just helps bring an open standard for how data should be represented and requests should be constructed.

JSON:API Resource vs REST Resources ?
The principal interest of codding a JSON:API Resource instead of a classic REST Resource is to leverage the same standardized {json:api} output in your custom resource than the output of Drupal JSON:API out-of-the-box collections endpoints.
Believe me, frontenders will prefer dealing with 1 specification than having to transform and specify customization for few unstandardized output.
“ Any fool can write code that a computer can understand. Good programmers write code that humans can understand. ”Martin Fowler
Another advantage of extending the JSON:API with your own collection is the Core ability that will be available out-of-the-box:
- Sorting
- Filtering
- Paginate
- Selection of fields
- Inclusion of relation
Deep Dive in Code
First, you will need to install a contrib module
drupal/jsonapi_resources.composer require drupal/jsonapi_resourcesDon’t worry, the module is trustable, tested and stable. Plus it has been made by mglaman, one of the core-contributor of JSON:API for Drupal.
Routing
Before starting your custom Query Builder, you will need to define you’re own new route.
Let's defines a route for a collection containing featured articles nodes.
# Defines a route for a collection containing featured articles nodes.
my_custom_resource.jsonapi_resources.featured_articles:
# %jsonapi% is a placeholder for the JSON:API base path, which can be
# configured in a site's services.yml file.
path: '/%jsonapi%/articles/featured'
defaults:
# Every JSON:API resource route must declare a _jsonapi_resource. The
# value can either be a class or a service ID. Unlike the _controller
# route default, it is not possible to declare a method name to be called.
_jsonapi_resource: Drupal\my_custom_resource\Resource\FeaturedArticlesResource
_jsonapi_resource_types: ['node--article']
requirements:
_permission: 'access content'In case you need adding options (eg. a
node or user), the same features as for common routes/controllers are available.# Defines a route for a collection containing related articles of given node.
path: '/%jsonapi%/articles/{page}/related'
defaults:
_jsonapi_resource: Drupal\my_custom_resource\Resource\RelatedArticlesOfPageResource
_jsonapi_resource_types: ['node--article']
requirements:
_permission: 'access content'
options:
parameters:
agent:
type: entity:nodeSee the Structure of routes for more informations.
The Resource
Processes a request for a collection containing featured articles Nodes.
<?php
namespace Drupal\my_custom_resource\Resource;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\jsonapi\ResourceResponse;
use Drupal\jsonapi_resources\Resource\EntityQueryResourceBase;
use Drupal\node\NodeInterface;
use Symfony\Component\HttpFoundation\Request;
/**
* Processes a request for a collection containing featured articles nodes.
*/
class FeaturedArticlesResource extends EntityQueryResourceBase {
/**
* Process the resource request.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* The request.
*
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
*
* @return \Drupal\jsonapi\ResourceResponse
* The response.
*/
public function process(Request $request): ResourceResponse {
$cacheability = new CacheableMetadata();
/** @var \Drupal\Core\Entity\ContentEntityType $entity_type */
$entity_type = $this->entityTypeManager->getDefinition('node');
/** @var string $bundle_field */
$bundle_field = $entity_type->getKey('bundle');
/** @var string $status_field */
$status_field = $entity_type->getKey('status');
$entity_query = $this->getEntityQuery('node')
->condition($bundle_field, 'article')
->condition('promote', TRUE)
->condition($status_field, NodeInterface::PUBLISHED);
$cacheability->addCacheContexts(['url']);
$paginator = $this->getPaginatorForRequest($request);
$paginator->applyToQuery($entity_query, $cacheability);
$data = $this->loadResourceObjectDataFromEntityQuery($entity_query, $cacheability);
$pagination_links = $paginator->getPaginationLinks($entity_query, $cacheability, TRUE);
/** @var \Drupal\jsonapi\CacheableResourceResponse $response */
$response = $this->createJsonapiResponse($data, $request, 200, [], $pagination_links);
$response->addCacheableDependency($cacheability);
return $response;
}
}“ Well done you did it ! ”Shia LaBeouf
That’s pretty much all the hocus-pocus that you need to have a custom JSON:API Resource that leverage JSON:API Core feature.

Digging Deeper
In case you need to upcast the URL parameter by UUID, I suggest you to use the converter
paramconverter.jsonapi.entity_uuid .# Defines a route for a collection containing related articles of given node.
path: '/%jsonapi%/articles/{page}/related'
defaults:
_jsonapi_resource: Drupal\my_custom_resource\Resource\RelatedArticlesOfPageResource
_jsonapi_resource_types: ['node--article']
requirements:
_permission: 'access content'
options:
parameters:
agent:
type: entity:node
# Upcast entity parameters by UUID.
converter: 'paramconverter.jsonapi.entity_uuid'Sources
For the most curious of you, here are some sources of additional information that inspired the creation of this article.
Gabe Sullice (20 September, 2019) A New Era for Drupal’s JSON:API.
Seen on: https://sullice.com/.../a-new-era-for-drupals-jsonapi
Seen on: https://sullice.com/.../a-new-era-for-drupals-jsonapi
Ivo Kovačević (03 Jully, 2019). How to create custom JSON-API resource drupal 8.
Seen on https://stackoverflow.com/.../how-to-create-custom-...
Seen on https://stackoverflow.com/.../how-to-create-custom-...
JSON:API Resources Documentation.
Seen on https://drupal.org/project/commerce/issues/2938731
Seen on https://drupal.org/project/commerce/issues/2938731
Dries Buytaert (11 February, 2019). Headless CMS: REST vs JSON:API vs GraphQL.
Seen on https://dri.es/headless-cms-rest-vs-jsonapi-vs-graphql
Seen on https://dri.es/headless-cms-rest-vs-jsonapi-vs-graphql
**JSON:API Search API Documentation.
**Seen on: https://drupal.org/project/jsonapi_search_api
**Seen on: https://drupal.org/project/jsonapi_search_api
Retrieve all the code on Gist:
https://gist.github.com/WengerK/99fdb2880ee0198ed4fe7d490f88bf7b
https://gist.github.com/WengerK/99fdb2880ee0198ed4fe7d490f88bf7b
Have a Drupal project? We can certainly help! Visit our page to learn more.
More articles
Author(s)
Making of : “La nuit des Losers”
lanuitdeslosers.ch, built fast: paper wireframes, a bold hand-drawn identity, then Claude Code to prototype and ship. A mouse-tracking hero animation, a bowling game made of sponsor logos, and a few bugs we’re keeping.
Author(s)
Dark mode: necessity or trend
Nous faisons le tri entre idées reçues, réalités techniques et bonnes pratiques de design.
Experiment #2 – Surway, a social experiments game
Pour notre deuxième expérimentation, nous avons relevé le défi de sortir de notre zone de confort en développant Surway, un jeu vidéo jouable directement dans le navigateur, où les actions de chaque joueur restent inscrites pour tous ceux qui suivront.













