Getting Started
rstore is a data store allowing you to handle all data in your application.
Define a data collection and then run queries or execute mutations (create, update and delete) on your data.
FEATURES
- Normalized reactive cache to ensure all components are up-to-date
- Co-locate queries within the components that need them
- Fully adaptable with plugins to fetch from any source (REST, GraphQL...)
- Scale down to small prototypes and scale up to big enterprise apps
- Query API designed for local-first and realtime
- Form API to handle form state and validation
- TypeScript support with full autocomplete
- Nuxt module with devtools
Vue
- Install rstore:
npm i @rstore/vue
pnpm i @rstore/vue
- Create some Collections:
import { defineCollection } from '@rstore/vue'
export const todoCollection = defineCollection({
name: 'todos',
// Interact with a REST/GraphQL/etc. API
hooks: {
fetchFirst: ({ key }) => fetch(`/api/todos/${key}`).then(r => r.json()),
fetchMany: ({ params }) => fetch('/api/todos').then(r => r.json()),
create: ({ item }) => { /* ... */ },
update: ({ key, item }) => { /* ... */ },
delete: ({ key }) => { /* ... */ },
},
})
import type { StoreSchema } from '@rstore/vue'
import { withItemType } from '@rstore/vue'
// Item type
export interface Todo {
id: string
text: string
completed: boolean
createdAt: Date
updatedAt?: Date
}
// Collection
const todoCollection = withItemType<Todo>().defineCollection({
name: 'todos',
})
export const schema = [
todoCollection,
] satisfies StoreSchema
INFO
Instead of defining the hooks in the collection, you can also create a plugin to handle the fetching logic for many collections at once (see Plugins).
- Create the store:
import { createStore } from '@rstore/vue'
import { todoCollection } from './collection'
import myPlugin from './plugin'
export async function setupRstore(app) {
const store = await createStore({
schema: [
todoCollection,
],
plugins: [
myPlugin,
],
})
}
import type { App } from 'vue'
import { createStore } from '@rstore/vue'
import { schema } from './collection'
import myPlugin from './plugin'
export async function setupRstore(app: App) {
const store = await createStore({
schema,
plugins: [
myPlugin,
],
})
}
- Install the store into the app:
import { RstorePlugin } from '@rstore/vue'
export async function setupRstore(app) {
const store = await createStore({
// ...
})
app.use(RstorePlugin, { store })
}
import { RstorePlugin, type VueStore } from '@rstore/vue'
export async function setupRstore(app: App) {
const store = await createStore({
// ...
})
app.use(RstorePlugin, { store })
}
// Augment the `useStore` type
declare module '@rstore/vue' {
export function useStore(): VueStore<typeof schema>
}
- Add the store to your app:
import { setupRstore } from './rstore'
app.use(setupRstore)
- Use the store in a component:
<script setup>
import { useStore } from '@rstore/vue'
const store = useStore()
const { data: todos } = store.todos.query(q => q.many())
</script>
<template>
<pre>{{ todos }}</pre>
</template>
Nuxt
The Nuxt module will automatically:
- scan the
app/rstore
folder in your Nuxt app for collections, - scan the
app/rstore/plugins
folder and register plugins (usingexport default
), - create the store
- handle SSR payload
- expose the
useStore
composable typed according to the collection (from therstore
folder).
- Install rstore and add it to the Nuxt config:
npm i @rstore/nuxt
pnpm i @rstore/nuxt
export default defineNuxtConfig({
modules: [
'@rstore/nuxt',
],
})
- Create some Collections in the
app/rstore
folder of your Nuxt app:
// One Collection
export default RStoreSchema.withItemType<Todo>().defineCollection({
name: 'todos',
// Interact with a REST/GraphQL/etc. API
hooks: {
fetchFirst: ({ key }) => $fetch(`/api/todos/${key}`),
fetchMany: ({ params }) => $fetch('/api/todos', { query: params }),
create: ({ item }) => { /* ... */ },
update: ({ key, item }) => { /* ... */ },
delete: ({ key }) => { /* ... */ },
},
})
// Multiple Collections
export const users = RStoreSchema.withItemType<User>().defineCollection({
name: 'users',
// Interact with a REST/GraphQL/etc. API
hooks: {
fetchFirst: ({ key }) => $fetch(`/api/users/${key}`),
fetchMany: ({ params }) => $fetch('/api/users', { query: params }),
create: ({ item }) => { /* ... */ },
update: ({ key, item }) => { /* ... */ },
delete: ({ key }) => { /* ... */ },
},
})
export const bots = RStoreSchema.withItemType<Bot>().defineCollection({
name: 'bots',
// Interact with a REST/GraphQL/etc. API
hooks: {
fetchFirst: ({ key }) => $fetch(`/api/bots/${key}`),
fetchMany: ({ params }) => $fetch('/api/bots', { query: params }),
create: ({ item }) => { /* ... */ },
update: ({ key, item }) => { /* ... */ },
delete: ({ key }) => { /* ... */ },
},
})
// Item types
export interface Todo {
id: string
text: string
completed: boolean
createdAt: Date
updatedAt?: Date
}
export interface User {
id: string
name: string
email: string
}
export interface Bot {
id: string
name: string
}
FILE SCANNING
The rstore module will only scan exports in files in the rstore
folder and not in nested folders. If you want to split the collections in multiple folders, you need to re-export each variables or use Nuxt layers (recommended).
INFO
Instead of defining the hooks in the collection, you can also create a plugin to handle the fetching logic for many collections at once (see Plugins).
Nuxt Layers
You can also add an app/rstore
folder in Nuxt layers! rstore will automatically add those files too.
- Use the store in a component:
<script setup>
const store = useStore()
const { data: todos } = await store.todos.query(q => q.many())
</script>
<template>
<pre>{{ todos }}</pre>
</template>
Open the Nuxt devtools and check the rstore
tab:
Nuxt + Drizzle
In case you are using Drizzle, you can install the @rstore/nuxt-drizzle
module instead of @rstore/nuxt
to automatically generate the collections and plugins from your drizzle schema.
- Install
@rstore/nuxt-drizzle
and add it to the Nuxt config:
npm i @rstore/nuxt-drizzle
export default defineNuxtConfig({
modules: [
'@rstore/nuxt-drizzle',
],
})
- Expose a function to return a drizzle instance:
// server/utils/drizzle.ts
import { drizzle } from 'drizzle-orm/libsql'
let drizzleInstance: ReturnType<typeof drizzle> | null = null
export function useDrizzle() {
drizzleInstance ??= drizzle({
connection: { url: useRuntimeConfig().dbUrl },
casing: 'snake_case',
})
return drizzleInstance
}
The module will automatically:
- load the drizzle schema from the
drizzle.config.ts
file (configurable with therstoreDrizzle.drizzleConfigPath
option in the Nuxt config), - generate the collections from the schema for each table with the relations,
- generate a REST API under the
/api/rstore
path to handle the CRUD operations, - generate a plugin to handle the queries and mutations,
- generate all the necessary types for the collections and the API.
You can already use the store in your components without any additional configuration:
<script setup>
const store = useStore()
const { data: todos } = await store.todos.query(q => q.many())
</script>
<template>
<pre>{{ todos }}</pre>
</template>
Continue to the plugin documentation ➜
Nuxt + Directus
You can use the @rstore/nuxt-directus
module to automatically generate the collections and plugins from your Directus backend.
npm install @rstore/nuxt-directus
export default defineNuxtConfig({
modules: [
'@rstore/nuxt-directus',
],
rstoreDirectus: {
url: 'https://your-directus-instance.com', // The URL of your Directus instance
adminToken: import.meta.env.DIRECTUS_TOKEN, // The admin token you created in step 2
},
})