rstore Documentation
repository·main·Indexed 19 days ago
https://github.com/directus/rstoreA local-first data store for Vue and Nuxt applications featuring a normalized reactive cache and a structured API for queries and mutations. It supports realtime, forms, and offline workflows with strong TypeScript support, a Nuxt module, and advanced features like operation batching and state serialization for SSR.
What's inside rstore
- rstore is a local-first data store designed for Vue and Nuxt applications. It provides a normalized reactive cache that is shared across your entire application, along with a structured API for queries and mutations. It is specifically built to support local-first, realtime, forms, and offline workflows, offering strong TypeScript support and a Nuxt module with DevTools integration.
What is rstore?
mainrstore is a data management library designed for efficient application data handling. It allows you to define data collections and perform queries or mutations (create, update, delete) seamlessly.
Key features include:
- Normalized reactive cache: Maintains a single source of truth and keeps UI components in sync.
- Adaptable plugin system: Supports fetching data from various sources like REST, GraphQL, or local storage.
- Local-first architecture: Prioritizes local data access and client-side computation (filtering/sorting) for speed and offline capabilities.
- TypeScript support: Provides full type safety and autocomplete.
Overview of @rstore/devtools
mainThe
@rstore/devtoolspackage allows you to embed the rstore Devtools UI into any application. It provides:- A prebuilt frontend served from a static route (default:
/__rstore). - A
RstoreDevtoolsVue component which acts as an iframe wrapper. - A
rstoreDevtoolsVite()plugin for Vite-based builds (including Nuxt).
- A prebuilt frontend served from a static route (default:
Integrate rstore with Directus using @rstore/directus
mainUse
@rstore/directusas the shared adapter layer to bridge rstore collections with the Directus runtime and schema. It provides helpers for Directus REST query options, cache-side filtering, singleton handling, and primary key stripping. It is designed to be used before writing custom Directus fetch or mutation code around rstore collections.Recommended Pairing:
- Pair with
rstore-vuefor collection, query, and form semantics. - Pair with framework-specific Directus skills (like Vite or Nuxt integrations) for wiring.
- Pair with
Choose your rstore setup
mainDepending on your application architecture, choose one of the following packages:
@rstore/vue: For standard Vue applications where you want explicit control over store creation and plugin registration.@rstore/nuxt: For Nuxt applications. Provides auto-registration, typeduseStore(), SSR integration, and DevTools support.@rstore/nuxt-drizzle: For Nuxt applications that already use a Drizzle schema. It generates rstore collections and matching server APIs from your existing schema.
Compare rstore with Pinia
mainrstore is a high-level state management library focused on a normalized reactive cache and comprehensive query/mutation/live APIs. In contrast, Pinia is a low-level state management library for Vue.js that provides no built-in structure for data fetching or caching, requiring developers to implement those features manually using the Vue Composition API.Understand the generated schema in @rstore/nuxt-monospace
mainAt Nuxt build time, the module processes the Monospace OpenAPI document and system schema metadata to generate rstore collections. This process produces:
getKeyfunctions: Derived from the true primary keys (ordered columns of the primary index, including composite and non-idkeys).- TypeScript interfaces: Generated from
*CollectionOutputschemas. - rstore relations: Configured to join on real Foreign Key (FK) constraint columns.
- Monospace collection metadata: Used by the REST plugin for runtime operations.
Note on Primary Keys: The
primaryKeysconfiguration is an override. If a collection has no primary index in the metadata and no override is provided, generation will fail with an explicit error because rstore cannot compute stable item keys.Filter plugins using Scope ID
mainThe
scopeIdproperty allows you to restrict which collections a plugin handles. By default, a plugin withscopeId: 'my-scope'will only intercept collections that also have themy-scopescope ID.To allow a plugin to intercept all collections regardless of their scope, use the
ignoreScope: trueoption when registering the hook.// Plugin only handles collections with 'my-scope' export default definePlugin({ name: 'my-plugin', scopeId: 'my-scope', setup({ hook }) { hook('fetchMany', async (payload) => { // This will only be called for collections with the scopeId 'my-scope' }) } }) // Plugin handles all collections regardless of scope export default definePlugin({ name: 'my-plugin', scopeId: 'my-scope', setup({ hook }) { hook('fetchMany', async (payload) => { // This will be called for all collections regardless of their scopeId }, { ignoreScope: true, }) } })Manage subscription lifecycles and avoid leaks
mainSubscriptions in
rstorefollow an option-driven lifecycle. When you initiate a subscription viasubscribe(), a connection or listener is established with the backend.Pitfall: Memory and Backend Leaks In long-lived contexts (such as Vue components that are not immediately destroyed or global services), failing to call the
unsubscribe()method returned by the subscription will result in leaked backend subscriptions. Always ensure thatawait sub.unsubscribe()is called when the subscription is no longer needed.How rstore-vue works: Core Concepts
mainrstore-vue provides a typed, cache-first data flow for Vue applications. The workflow follows these mental models:
- Schema Definition: Use
withItemType(...).defineCollection(...)to create typed collection contracts anddefineRelations(...)to define normalized cross-collection relations. - Store Creation:
createStore({ schema, plugins, ... })builds the core engine, including the cache, hook system, and per-collection API proxies. - Injection: Use
RstorePluginto inject the store into Vue components, orsetActiveStore(store)for non-component contexts like tests. - Data Access: Access collections via
store.<collection>orstore.$collection(name). UsequeryorliveQueryfor reactive data flows, andfind*for one-shot async reads. - Mutations: Use
createFormorupdateFormfor UI-driven mutations, which handle validation and change tracking. - Extensibility: Use
definePluginto extend fetch, cache, or subscription behavior, anddefineModuleto create reusable, store-scoped logic.
- Schema Definition: Use
Configure Optimistic Updates
mainBy default, rstore performs optimistic updates: the store is updated immediately before the server confirms the change. If the mutation fails, the change is automatically reverted.
Disabling Optimistic Updates
Pass
{ optimistic: false }to any mutation method to wait for server confirmation before updating the local store.Customizing Optimistic Data
You can provide a custom object to the
optimisticoption. This object is merged with your mutation data and can be used to provide temporary values (like a temporary ID or timestamp) before the server responds.Checking Optimistic State
You can check if a record is currently in an optimistic state using the
$isOptimisticproperty.// Disable optimistic updates await store.todos.create({ title: 'No Optimism' }, { optimistic: false }) // Provide custom optimistic data const newTodo = await store.todos.create({ title: 'New Todo', completed: false, }, { optimistic: { id: 'temp-id', createdAt: new Date().toISOString(), }, }) // Check if a record is optimistic if (todo.$isOptimistic) { console.log('This record is optimistic') }Compare rstore modules with Pinia
mainWhile both manage state, rstore modules differ from Pinia in several ways:
Feature rstore Modules Integration Designed for seamless use with rstore data collections and devtools Private State Supports private state that is still SSR-compatible Async/Hybrid Can be awaited for async initialization; supports a 'hybrid promise' pattern where properties are available even without awaiting Pinia External library, synchronous stores