The DruxtStore
The DruxtStore caches Drupal collections and resources as shared references, so two components asking for the same article hit Drupal once.
The DruxtStore is a Vuex module that acts as Druxt's shared data layer:
every module (entity, menu, views, router) reads from and writes to it. It
sits between the components and the
DruxtClient, and its job is to make sure
Drupal is asked as little as possible.
This page explains the store's shape, its caching rules, and the collections/resources model that makes cross-module data sharing work.
Two maps, not one
The state is two plain objects, keyed differently on purpose:
state: () => ({
collections: {}, // collections[type][hash][prefix]
resources: {}, // resources[type][id][prefix]
});resourcesis keyed by identity: resourcetypeandid. A resource has exactly one home here, no matter how many different queries returned it.collectionsis keyed by query: the resourcetype, ahashderived from the query, and the languageprefix.
The prefix key is the JSON:API language prefix (such as es), so
translated variants of the same resource coexist rather than overwrite each
other.
Dehydration: collections store references
When a collection arrives from Drupal, addCollection does something that
surprises people the first time: it strips the resource data out of the
collection. Each item is committed individually to resources, and the
collection's data array is left holding only { id, type } references.
If an article appears in both a listing and a fetch of the same article by UUID, its attributes and relationships are stored once. That's the payoff.
Included resources (?include=...) go through the same path: a query that
brings in an article's image also populates resources for the image and
its file, ready for whichever component needs them next.
Rehydrating reverses the trick: getCollection reads the references back
out of the collection, included as well as data, and joins them against
resources before returning. A resource flushed from resources is fetched
again on the next collection request.
Caching rules
Collections: the query hash
getCollection hashes the query (everything except fields and include)
with MD5 to form the cache key. Calls with the same filter, sort and
pagination share an entry. Adding bypassCache: true skips the check and
re-requests.
Resources: full and partial flags
Resources are cached with a completeness flag. When a fields[...]
filter narrowed the query, the stored resource is marked _druxt_partial
with a timestamp; a full fetch is marked _druxt_full. This distinction
lets getResource serve a cached partial when only those fields are needed,
while recognizing that a complete render needs more.
Includes are recursive
getResource hydrates ?include= relationships by dispatching getResource
for each related resource, including nested includes
(include=media,media.image). Missing pieces are fetched; present ones are
reused.
Requests in flight are shared
Concurrent dispatches for the same resource or collection share one backend request. A failed request is not kept, so the next dispatch tries again.
Clearing everything
druxt/clearCache empties the client, every stored resource, collection,
route, Views result, menu and schema, and the server cache between
requests. It clears the process it runs in: a dispatch in the browser
leaves the server alone, and Drupal clears the server through the endpoint
on Cache Drupal responses on the Nuxt server.
flushResource and flushCollection remove what they name and do nothing
for data that was never stored. flushCollection takes the query that
stored the collection, not a hash, and both take a type with its prefix.
A flush also drops the requests in flight, so a dispatch made after it
fetches again. A dispatch made before it still gets its document, but
nothing from it is stored.
Using the store
Components rarely talk to the client directly; they dispatch actions:
// One article, by type and UUID.
const resource = await this.$store.dispatch('druxt/getResource', {
type: 'node--article',
id: 'd8dfd355-7f2f-4fc3-a149-288e4e293bdd',
});
// All articles matching a query.
const collection = await this.$store.dispatch('druxt/getCollection', {
type: 'node--article',
query: new DrupalJsonApiParams().addFilter('status', '1'),
});Both accept a prefix (language) and bypassCache. Mutations exist for the
other direction (addResource, addCollection, flushResource,
flushCollection) and are the tool for dropping one entry. druxt/clearCache drops
everything.
The mutation and action reference lists
every signature; retired signatures are tracked on the
deprecations page.
The case for a store
A Nuxt component could fetch JSON:API directly. Using a store instead pays off three ways:
- Request deduplication across components and modules: one Drupal hit per unique resource per render pass, however many components ask for it at once. Between render passes, a production server keeps the index and the menus for as long as Drupal allows, and Drupal says no for every signed-in request. Cache Drupal responses on the Nuxt server covers that cache.
- A stable identity for data whose representation varies by query: the references model keeps partial and full views coherent.
- A seam for SSR: cached state serializes with the page, so a server-rendered component does not fetch its data again in the browser.