Change JSON:API routes from every bundle/field to per entity with arguments

Created on 6 June 2025, about 2 months ago

Problem/Motivation

I'm doing a performance audit on a fairly complex site. It uses webform module and has over 400 webform response bundles (e.g. webform makes one entity bundle per webform), and it also has JSON:API installed.

I noticed that router rebuild was taking a long time, and most of this is spent in JSON:API. It turns out that webform makes an individual route for each resource, and there are multiple resources per bundle

So you have routes like

json.api.webform_submission--bundle1_name.webform_id.related
json.api.webform_submission--bundle1_name.webform_id.relationship.delete
json.api.webform_submission--bundle1_name.uid.related
json.api.webform_submission--bundle2_name.webform_id.related
json.api.webform_submission--bundle2_name.webform_id.relationship.delete
json.api.webform_submission--bundle2_name.uid.related

Overall, on this specific site it results in over 17,000 JSON:API routes once nodes, taxonomy terms etc. are added in.

I think the number of bundles on this site is quite high, but it seems very likely that sites could easily hit thousands of of JSON:API routes with a less extreme dataset, even if it's 2,000 routes instead of 17,000.

Steps to reproduce

Install JSON:API on a site with lots of bundles, and run

mysql > SELECT name FROM router WHERE name LIKE 'jsonapi%';

Proposed resolution

Instead of a route-per-resource, I think JSON:API could make a handful of routes that take bundle and field as arguments, instead of encoding them in the route ID. The route controller and access checks would then have to take the entity type, bundle, and field, and figure out the resource from them but after that everything could work more or less the same. Not sure if the entity type should itself be a route parameter or route-per-entity type, but something along those lines.

JSON:API extras ( https://www.drupal.org/project/jsonapi_extras ) provides a way to enable/disable routes per resource, this would probably have to be adapted to use bundle parameters or similar: https://www.drupal.org/node/3155569

Remaining tasks

User interface changes

Introduced terminology

API changes

Data model changes

Release notes snippet

📌 Task
Status

Active

Version

11.0 🔥

Component

jsonapi.module

Created by

🇬🇧United Kingdom catch

Live updates comments and jobs are added and updated live.
  • Performance

    It affects performance. It is often combined with the Needs profiling tag.

Sign in to follow issues

Comments & Activities

  • Issue created by @catch
  • I saw this issue in Slack and had a vague recollection of being the culprit for this routing approach. So I dug up this issue from when JSON:API was in contrib: https://www.drupal.org/project/jsonapi/issues/2953346

    There's some nuance, the routes aren't per-field, they're per relationship field, i.e., basically per entity reference field (with some exceptions).

    The trade-off is that when the field name is a param, all the logic for whether the field is a relationship, whether it supports the request method, and whether it's a file field happens at request time.

    Back then, that process was a source of instability and it was preferable to have those bugs surface during a router rebuild rather than "in the field".

    Now, a lot of that logic lives in the resource type repository so it might not be computed on every request any more. I can't recall if we're caching those results.

    To be clear, I'm not saying this is a bad idea, I'm just adding that historical context so that we don't introduce a regression—tearing down Chesterton's fence so to speak.

Production build 0.71.5 2024