← all posts
2026.08.12 · 6 min

JSON:API Views 8.x-1.2

DrupalDruxtPlanet Drupal

JSON:API on its own gets you decoupled entities: fetch a node, fetch a set of IDs, done. It doesn't get you the filtering, sorting and pagination that most real content listings actually need - a list of articles by tag, a paginated product catalogue, an events calendar with a date filter. You either reimplement that logic on the frontend, or Drupal ships a hand-written custom resource for every list on the site. That gap is the same regardless of what's rendering on the other end, React and Next.js, Vue and Nuxt, or anything else that can call an API.

JSON:API Views closes it: whatever a View can already do, a decoupled frontend can ask for over JSON:API, using the access checks and query logic the view already has, no separate endpoint to write or maintain. It's also, not coincidentally, why I still maintain the module at all: it's the backend half of DruxtViews, the piece that makes Views work in Druxt, my own Nuxt-based decoupled Drupal framework.

8.x-1.2 lands six fixes, five of which already had a patch sitting in the queue: route building that's dramatically faster on sites with a lot of exposed views, a preview URL that finally matches what an exposed filter is actually configured to accept, a caching fix so front-end caches actually invalidate, a routing crash tied to numeric bundle names, and a reworked fix for an OpenAPI export crash. Just fixes, no updating required.

JSON:API Views isn't mine originally. pixelwhip built the module and shipped its first releases; I took over maintenance after that, and 8.x-1.2 is the first release I've cut on the 1.x line. Thanks to pixelwhip for building the thing in the first place.

If you haven't used it before

Views is what most Drupal sites already lean on for anything with filtering, sorting or pagination baked in: a list of articles by tag, a paginated product catalogue, an events calendar with a date filter. JSON:API on its own gets a decoupled frontend entities, but it doesn't know about any of that. JSON:API Views bridges the two: enable it on a display and Drupal generates a route for it under /jsonapi/views/{view_id}/{display_id}, backed by the same query, filters and access checks the display already has.

Every display ships exposed by default. One checkbox turns it off (for now, see What's next below).

The preview panel in the Views UI picks it up too, once a display is exposed: the same query info table that already shows you the page's path grows a JSON:API Views row with the live URL for that display, arguments and exposed filter values included.

The generated JSON:API endpoint for a view, right there in the Views UI preview panel.

Hit that URL and it's a normal JSON:API response, whatever content types the view actually mixes together:

GET /jsonapi/views/frontpage/page_1
{
  "data": [
    {
      "type": "node--page",
      "id": "19e113d5-c838-43e1-ba75-1def5466e3de",
      "attributes": {
        "title": "About this site",
        "status": true,
        "created": "2026-08-09T09:07:37+00:00"
      }
    },
    {
      "type": "node--article",
      "id": "62c2d25d-93c9-438b-8bdf-a75b69d05dc5",
      "attributes": {
        "title": "Building decoupled Views with jsonapi_views",
        "status": true,
        "created": "2026-08-08T10:18:06+00:00"
      }
    }
  ],
  "meta": { "count": "2" }
}

On the frontend, Druxt's <DruxtView> component renders that exact route as Vue components, filters, sorting and pagination included, no custom resource on either side. It's the frontend half of the same pipeline. DruxtViews targets Nuxt 2 today; a Nuxt 3/4-compatible Druxt is actively in progress, more on that soon.

Render a View with Druxt
<DruxtView view-id="frontpage" display-id="page_1" />

What's fixed

Five bug fixes, with a big thanks to the community whose patches made most of them possible:

Cache headers weren't being set (#3202583, patch by yahyaalhamad). View cache tags weren't reaching the JSON:API response's headers, so a page cache in front of the site never invalidated when the underlying content changed. Fixed by pushing the tags onto the response's bubbleable metadata.

Route building got slow with a lot of exposed views (#3484714, patch by jacobbell84). Every rebuild looked up each view's resource type from scratch, once per view per entity type. Caching it per entity type instead: 637 calls to ResourceTypeRepository::get() down to 13, at the scale the issue reported.

Numeric bundle machine names broke routing entirely (#3503402, patch by chodec). An all-digit bundle machine name threw an AssertionError during route building instead of returning a route. Cast to string before the lookup, fixed.

The preview URL used the wrong query parameter (#3376193, patch by jeffschuler). The Views UI preview panel was reading an exposed filter's field name instead of its configured identifier, breaking any view where you'd renamed a filter to something readable. It now reads the identifier the display is actually configured with.

OpenAPI schema export crashed with jsonapi_views enabled (#3265781). jsonapi_views now reports a view's real ResourceType on the route when its entity type has exactly one bundle, so OpenAPI describes it correctly; multi-bundle views are left alone.

What's next

This is part of the same push that got Field Tokens 2.0.0 and Custom Formatters 4.1.0 out the door: working through my modules one at a time and actually clearing the backlog, not just triaging it. There's more coming for JSON:API Views too, tighter access control at the route level, more performance headroom for sites well past a hundred exposed views, and eventually a 2.0.x that flips exposure from opt-out to opt-in. File (Field) Paths is next up though, tens of thousands of sites and still sitting in RC ahead of a stable 1.0.

Worth calling out

If your project depends on this work

JSON:API Views, the wider Druxt ecosystem, the other modules I maintain - I build all of it in the open, and sponsorship is what keeps it actively maintained.

Sponsor on GitHub ↗

If any of this is useful to you, 8.x-1.2 is out now:

composer require 'drupal/jsonapi_views:^1.2'
Source on GitHub, project page on Drupal.org, patches welcome in the issue queue. If your project depends on this, sponsoring keeps it actively maintained.

What's your decoupled stack - are you hand-rolling a listing endpoint for every view, or reaching for something like this? I'd genuinely like to know.

Discussion

via GitHub Discussions