Rstore Nuxt Drizzle
Generate rstore collections and API/runtime behavior from Drizzle schema metadata, then add realtime/offline and hook-based server controls as needed.
Use this skill with the @rstore/nuxt skill for Nuxt module/runtime behavior and with the @rstore/vue skill for underlying collection/query/form semantics.
Documentation map
Core concepts
| Primitive |
Purpose |
rstoreDrizzle.drizzleConfigPath |
Locates Drizzle config file loaded by the module |
drizzleConfig.schema |
Must be a single importable schema file path |
drizzleImport |
Defines server-side drizzle getter import used by generated handlers |
#build/$rstore-drizzle-collections |
Generated collections with inferred keys, meta, and relations |
apiPath |
Base REST route for generated CRUD handlers |
ws |
Enables websocket realtime handler and client plugin |
offline |
Enables offline sync plugins and sync config template values |
rstoreDrizzleHooks / hooksForTable / allowTables |
Server extension and access-control APIs |
Quick start
export default defineNuxtConfig({
modules: ['@rstore/nuxt-drizzle'],
rstoreDrizzle: {
drizzleImport: {
name: 'useDrizzle',
from: '~~/server/utils/drizzle',
},
apiPath: '/api/rstore',
},
})
Also provide the server import the module expects by default:
// server/utils/drizzle.ts
export function useDrizzle() {
// return your drizzle instance
}
Task workflow
- Confirm
drizzle.config.ts exists and exports a config with string schema.
- Configure
drizzleImport if not using ~~/server/utils/drizzle with useDrizzle.
- Let the module generate collections and handlers; avoid parallel manual CRUD layers.
- Query through rstore collection APIs using
findOptions.where and supported drizzle params.
- Enable
ws and/or offline only when required by product behavior.
- Add server-side restrictions/transforms via hook APIs (
hooksForTable, allowTables, rstoreDrizzleHooks).
- When adding a new Drizzle table to a project that already calls
allowTables, register the new table in the same allowTables([...]) list — once initialized the allow-list is permanent and unlisted tables throw Collection "<name>" is not allowed..
- For Nuxt module/runtime integration behavior, use the
rstore-nuxt skill.
- For non-drizzle-specific store/query/form behavior, use the
rstore-vue skill.
When you are tempted to write a custom endpoint
Before adding a server/api/*.ts handler, a defineEventHandler, or any custom REST route that touches a Drizzle table, decide which case applies:
- Plain CRUD for an existing Drizzle table → stop. Use the generated endpoints under
apiPath. Add logic via hooksForTable(table, { 'item.beforeCreate': ... }) or the matching *.before / *.after hook — don't fork into a parallel route.
- Row-level access control, tenant scoping, soft-delete filters → use
allowTables([...]) plus hooksForTable with *.before hooks calling transformQuery(({ where, extras }) => ...). A custom route would bypass both guards.
- Bulk or direct Drizzle write the generated endpoints cannot express (multi-table transaction, raw SQL, migration-style script) → a custom route is fine, but call
publishRstoreDrizzleRealtimeUpdate after the write so liveQuery subscribers stay in sync. See the Nuxt + Drizzle docs section on "Publishing realtime updates from direct Drizzle queries".
- Non-CRUD RPC (trigger an external workflow, send an email, compute a derived value) → custom route is appropriate; it is outside rstore's scope.
Query and cache conventions
- Prefer
findOptions.where over the deprecated params.where.
- Use
findOptions.include for relation loading; relation include objects support where, orderBy, columns, limit, and nested include.
- Use
params.limit, offset, columns, orderBy, and keys to shape Drizzle-backed queries.
- Use
params.with only as a low-level Drizzle override; when both are provided, params.with takes precedence over findOptions.include.
- Query params and request bodies are serialized with
SuperJSON, so keep them serializable.
- The runtime plugin parses
createdAt and updatedAt string values into Date objects through collection defaults.
fetchRelations translates included relations into follow-up equality queries against the generated target collections.
- Cache filtering for
findFirst and findMany reuses the same where and orderBy semantics client-side.
Realtime and offline behavior
ws: true (or object form) enables websocket handler registration and client subscription plugin wiring.
- Realtime subscriptions are keyed by collection, key, and
where; exact filter shape impacts topic reuse.
- On websocket reconnect, the runtime re-sends active subscriptions and triggers
realtimeReconnectEventHook, which makes liveQuery refresh.
offline enables offline plugin generation and sync config wiring.
- Offline sync expects stable keys and usable
updatedAt comparison values.
offline.serializeDateValue exists for non-default date comparison serialization.
Server extension points
- Use
hooksForTable(table, hooks) to scope API hooks to a specific Drizzle table.
- Use
rstoreDrizzleHooks.hook(...) when the extension needs to work across multiple tables.
*.before hooks can call transformQuery(({ where, extras }) => ...) to add constraints before execution.
- Use
allowTables([...]) to deny access to unlisted generated collections.
- Use the
realtime.filter hook to reject websocket updates for a peer when row-level rules apply.
Guardrails
- If drizzle config is missing, module setup is skipped after warning.
- Non-string
schema in drizzle config throws.
- Multi-field relations/references are not supported by current relation inference and throw.
- Renaming schema exports renames generated collection names.
- Composite keys serialize as
value1::value2; mismatches here cause lookup/update issues.
params.where is deprecated; use findOptions.where.
allowTables flips the default from "all tables exposed" to "deny by default". After the first call, every new Drizzle table you add to the schema must also be added to allowTables — otherwise endpoints throw Collection "<name>" is not allowed. at runtime.
- Do not hand-write
server/api/<table>/* CRUD routes for tables already exposed by the generated apiPath. Duplicate code paths drift, bypass allowTables / hooksForTable, and miss realtime publishing — extend behavior through hooks or use publishRstoreDrizzleRealtimeUpdate from a justified custom route.
References
Further reading
Source: directus/rstore — distributed by TomeVault.
1---2name: rstore-nuxt-drizzle3description: Use when exposing Drizzle-backed data in Nuxt, OR before writing a custom `server/api` route, Nitro `defineEventHandler`, H3 handler, or REST/CRUD endpoint that reads or writes a Drizzle table — prefer the module's generated endpoints, `allowTables`, `hooksForTable`, and `publishRstoreDrizzleRealtimeUpdate` over hand-rolled routes; also covers generating collections/API routes from schema, adding a new Drizzle table to the rstore API, fixing `Collection \"<name>\" is not allowed` errors, fetch/filter/paginate, create/update/delete, realtime, offline, and table-level access control; pair with `rstore-nuxt` for Nuxt integration and `rstore-vue` for collection/query/form behavior.4---56# Rstore Nuxt Drizzle78Generate rstore collections and API/runtime behavior from Drizzle schema metadata, then add realtime/offline and hook-based server controls as needed.9Use this skill with the `@rstore/nuxt` skill for Nuxt module/runtime behavior and with the `@rstore/vue` skill for underlying collection/query/form semantics.1011## Documentation map1213| Area | Documentation |14| --- | --- |15| Nuxt + Drizzle plugin overview | [https://rstore.akryum.dev/plugins/nuxt-drizzle](https://rstore.akryum.dev/plugins/nuxt-drizzle) |16| Query and filter model | [https://rstore.akryum.dev/guide/data/query](https://rstore.akryum.dev/guide/data/query) |17| Relations behavior | [https://rstore.akryum.dev/guide/schema/relations](https://rstore.akryum.dev/guide/schema/relations) |18| Realtime subscriptions | [https://rstore.akryum.dev/guide/data/live](https://rstore.akryum.dev/guide/data/live) |19| Offline behavior | [https://rstore.akryum.dev/guide/data/offline](https://rstore.akryum.dev/guide/data/offline) |20| Plugin hooks and extension points | [https://rstore.akryum.dev/guide/plugin/hooks](https://rstore.akryum.dev/guide/plugin/hooks) |21| Related package skills | `rstore-nuxt` skill (`@rstore/nuxt`), `rstore-vue` skill (`@rstore/vue`) |22| Skill-local API references | [./references/index.md](./references/index.md) |2324## Core concepts2526| Primitive | Purpose |27| --- | --- |28| `rstoreDrizzle.drizzleConfigPath` | Locates Drizzle config file loaded by the module |29| `drizzleConfig.schema` | Must be a single importable schema file path |30| `drizzleImport` | Defines server-side drizzle getter import used by generated handlers |31| `#build/$rstore-drizzle-collections` | Generated collections with inferred keys, meta, and relations |32| `apiPath` | Base REST route for generated CRUD handlers |33| `ws` | Enables websocket realtime handler and client plugin |34| `offline` | Enables offline sync plugins and sync config template values |35| `rstoreDrizzleHooks` / `hooksForTable` / `allowTables` | Server extension and access-control APIs |3637## Quick start3839```ts40export default defineNuxtConfig({41 modules: ['@rstore/nuxt-drizzle'],42 rstoreDrizzle: {43 drizzleImport: {44 name: 'useDrizzle',45 from: '~~/server/utils/drizzle',46 },47 apiPath: '/api/rstore',48 },49})50```5152Also provide the server import the module expects by default:5354```ts55// server/utils/drizzle.ts56export function useDrizzle() {57 // return your drizzle instance58}59```6061## Task workflow62631. Confirm `drizzle.config.ts` exists and exports a config with string `schema`.642. Configure `drizzleImport` if not using `~~/server/utils/drizzle` with `useDrizzle`.653. Let the module generate collections and handlers; avoid parallel manual CRUD layers.664. Query through rstore collection APIs using `findOptions.where` and supported drizzle params.675. Enable `ws` and/or `offline` only when required by product behavior.686. Add server-side restrictions/transforms via hook APIs (`hooksForTable`, `allowTables`, `rstoreDrizzleHooks`).697. When adding a new Drizzle table to a project that already calls `allowTables`, register the new table in the same `allowTables([...])` list — once initialized the allow-list is permanent and unlisted tables throw `Collection "<name>" is not allowed.`.708. For Nuxt module/runtime integration behavior, use the `rstore-nuxt` skill.719. For non-drizzle-specific store/query/form behavior, use the `rstore-vue` skill.7273## When you are tempted to write a custom endpoint7475Before adding a `server/api/*.ts` handler, a `defineEventHandler`, or any custom REST route that touches a Drizzle table, decide which case applies:7677- **Plain CRUD for an existing Drizzle table** → stop. Use the generated endpoints under `apiPath`. Add logic via `hooksForTable(table, { 'item.beforeCreate': ... })` or the matching `*.before` / `*.after` hook — don't fork into a parallel route.78- **Row-level access control, tenant scoping, soft-delete filters** → use `allowTables([...])` plus `hooksForTable` with `*.before` hooks calling `transformQuery(({ where, extras }) => ...)`. A custom route would bypass both guards.79- **Bulk or direct Drizzle write the generated endpoints cannot express** (multi-table transaction, raw SQL, migration-style script) → a custom route is fine, but **call `publishRstoreDrizzleRealtimeUpdate`** after the write so `liveQuery` subscribers stay in sync. See the Nuxt + Drizzle docs section on "Publishing realtime updates from direct Drizzle queries".80- **Non-CRUD RPC** (trigger an external workflow, send an email, compute a derived value) → custom route is appropriate; it is outside rstore's scope.8182## Query and cache conventions8384- Prefer `findOptions.where` over the deprecated `params.where`.85- Use `findOptions.include` for relation loading; relation include objects support `where`, `orderBy`, `columns`, `limit`, and nested `include`.86- Use `params.limit`, `offset`, `columns`, `orderBy`, and `keys` to shape Drizzle-backed queries.87- Use `params.with` only as a low-level Drizzle override; when both are provided, `params.with` takes precedence over `findOptions.include`.88- Query params and request bodies are serialized with `SuperJSON`, so keep them serializable.89- The runtime plugin parses `createdAt` and `updatedAt` string values into `Date` objects through collection defaults.90- `fetchRelations` translates included relations into follow-up equality queries against the generated target collections.91- Cache filtering for `findFirst` and `findMany` reuses the same `where` and `orderBy` semantics client-side.9293## Realtime and offline behavior9495- `ws: true` (or object form) enables websocket handler registration and client subscription plugin wiring.96- Realtime subscriptions are keyed by collection, key, and `where`; exact filter shape impacts topic reuse.97- On websocket reconnect, the runtime re-sends active subscriptions and triggers `realtimeReconnectEventHook`, which makes `liveQuery` refresh.98- `offline` enables offline plugin generation and sync config wiring.99- Offline sync expects stable keys and usable `updatedAt` comparison values.100- `offline.serializeDateValue` exists for non-default date comparison serialization.101102## Server extension points103104- Use `hooksForTable(table, hooks)` to scope API hooks to a specific Drizzle table.105- Use `rstoreDrizzleHooks.hook(...)` when the extension needs to work across multiple tables.106- `*.before` hooks can call `transformQuery(({ where, extras }) => ...)` to add constraints before execution.107- Use `allowTables([...])` to deny access to unlisted generated collections.108- Use the `realtime.filter` hook to reject websocket updates for a peer when row-level rules apply.109110## Guardrails1111121. If drizzle config is missing, module setup is skipped after warning.1132. Non-string `schema` in drizzle config throws.1143. Multi-field relations/references are not supported by current relation inference and throw.1154. Renaming schema exports renames generated collection names.1165. Composite keys serialize as `value1::value2`; mismatches here cause lookup/update issues.1176. `params.where` is deprecated; use `findOptions.where`.1187. `allowTables` flips the default from "all tables exposed" to "deny by default". After the first call, every new Drizzle table you add to the schema must also be added to `allowTables` — otherwise endpoints throw `Collection "<name>" is not allowed.` at runtime.1198. Do not hand-write `server/api/<table>/*` CRUD routes for tables already exposed by the generated `apiPath`. Duplicate code paths drift, bypass `allowTables` / `hooksForTable`, and miss realtime publishing — extend behavior through hooks or use `publishRstoreDrizzleRealtimeUpdate` from a justified custom route.120121## References122123| Topic | Description | Reference |124| --- | --- | --- |125| API index | Full map of Nuxt-Drizzle API/config references | [api-index](./references/index.md) |126| rstoreDrizzle.drizzleConfigPath | Drizzle config lookup path | [api-drizzle-config-path](./references/api-drizzle-config-path.md) |127| rstoreDrizzle.drizzleImport | Server drizzle getter import contract | [api-drizzle-import](./references/api-drizzle-import.md) |128| rstoreDrizzle.apiPath | Generated REST base path | [api-api-path](./references/api-api-path.md) |129| rstoreDrizzle.ws | Enable websocket realtime integration | [api-ws](./references/api-ws.md) |130| rstoreDrizzle.ws.apiPath | Override websocket endpoint path | [api-ws-api-path](./references/api-ws-api-path.md) |131| rstoreDrizzle.offline | Enable offline sync integration | [api-offline](./references/api-offline.md) |132| rstoreDrizzle.offline.serializeDateValue | Customize offline sync date serialization | [api-offline-serialize-date-value](./references/api-offline-serialize-date-value.md) |133| findOptions.include | Primary relation include option | [api-find-options-include](./references/api-find-options-include.md) |134| findOptions.where | Primary drizzle filter option | [api-find-options-where](./references/api-find-options-where.md) |135| params.where (deprecated) | Legacy filter location | [api-params-where](./references/api-params-where.md) |136| params.limit | Limit rows in list queries | [api-params-limit](./references/api-params-limit.md) |137| params.offset | Offset rows in list queries | [api-params-offset](./references/api-params-offset.md) |138| params.with | Low-level Drizzle relation override | [api-params-with](./references/api-params-with.md) |139| params.columns | Selected column projection | [api-params-columns](./references/api-params-columns.md) |140| params.orderBy | Sort order format and behavior | [api-params-order-by](./references/api-params-order-by.md) |141| params.keys | Key-constrained list fetches | [api-params-keys](./references/api-params-keys.md) |142| filterWhere | Local cache condition evaluator | [api-filter-where](./references/api-filter-where.md) |143| rstoreDrizzleHooks | Global server/realtime hook bus | [api-rstore-drizzle-hooks](./references/api-rstore-drizzle-hooks.md) |144| hooksForTable | Table-scoped hook registration helper | [api-hooks-for-table](./references/api-hooks-for-table.md) |145| allowTables | Collection allow-list access control | [api-allow-tables](./references/api-allow-tables.md) |146| publishRstoreDrizzleRealtimeUpdate | Publish manual realtime updates for direct Drizzle writes | [api-publish-rstore-drizzle-realtime-update](./references/api-publish-rstore-drizzle-realtime-update.md) |147| Base @rstore/nuxt skill | Nuxt module/runtime integration semantics | `rstore-nuxt` skill |148| Base @rstore/vue skill | Underlying collection/query/form semantics | `rstore-vue` skill |149150## Further reading151152- Nuxt + Drizzle docs: [https://rstore.akryum.dev/plugins/nuxt-drizzle](https://rstore.akryum.dev/plugins/nuxt-drizzle)153- Query docs: [https://rstore.akryum.dev/guide/data/query](https://rstore.akryum.dev/guide/data/query)154- Live docs: [https://rstore.akryum.dev/guide/data/live](https://rstore.akryum.dev/guide/data/live)155- Offline docs: [https://rstore.akryum.dev/guide/data/offline](https://rstore.akryum.dev/guide/data/offline)156- @rstore/nuxt skill: `rstore-nuxt`157- @rstore/vue skill: `rstore-vue`158159---160> Source: [directus/rstore](https://github.com/directus/rstore) — distributed by [TomeVault](https://tomevault.io).161<!-- tomevault:4.0:skill_md:2026-06-17 -->