directus/rstore/rstore-nuxt-drizzle
Claude Code and agent skill by directus/rstore
- community
Checking that the SKILL.md file is still in the repository.
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.
Install
npx vskill@latest install directus/rstore/rstore-nuxt-drizzleSKILL.md
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
| Area | Documentation | |
|---|---|---|
| Nuxt + Drizzle plugin overview | https://rstore.akryum.dev/plugins/nuxt-drizzle | |
| Query and filter model | https://rstore.akryum.dev/guide/data/query | |
| Relations behavior | https://rstore.akryum.dev/guide/schema/relations | |
| Realtime subscriptions | https://rstore.akryum.dev/guide/data/live | |
| Offline behavior | https://rstore.akryum.dev/guide/data/offline | |
| Plugin hooks and extension points | https://rstore.akryum.dev/guide/plugin/hooks | |
| Related package skills | rstore-nuxt skill (@rstore/nuxt), rstore-vue skill (@rstore/vue) | |
| Skill-local API references | ./references/index.md |
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
Also provide the server import the module expects by default:
Task workflow
- Confirm
drizzle.config.tsexists and exports a config with stringschema. - Configure
drizzleImportif not using~~/server/utils/drizzlewithuseDrizzle. - Let the module generate collections and handlers; avoid parallel manual CRUD layers.
- Query through rstore collection APIs using
findOptions.whereand supported drizzle params. - Enable
wsand/orofflineonly 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 sameallowTables([...])list — once initialized the allow-list is permanent and unlisted tables throwCollection "<name>" is not allowed.. - For Nuxt module/runtime integration behavior, use the
rstore-nuxtskill. - For non-drizzle-specific store/query/form behavior, use the
rstore-vueskill.
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 viahooksForTable(table, { 'item.beforeCreate': ... })or the matching*.before/*.afterhook — don't fork into a parallel route. - Row-level access control, tenant scoping, soft-delete filters → use
allowTables([...])plushooksForTablewith*.beforehooks callingtransformQuery(({ 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
liveQuerysubscribers 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.whereover the deprecatedparams.where. - Use
findOptions.includefor relation loading; relation include objects supportwhere,orderBy,columns,limit, and nestedinclude. - Use
params.limit,offset,columns,orderBy, andkeysto shape Drizzle-backed queries. - Use
params.withonly as a low-level Drizzle override; when both are provided,params.withtakes precedence overfindOptions.include. - Query params and request bodies are serialized with
SuperJSON, so keep them serializable. - The runtime plugin parses
createdAtandupdatedAtstring values intoDateobjects through collection defaults. fetchRelationstranslates included relations into follow-up equality queries against the generated target collections.- Cache filtering for
findFirstandfindManyreuses the samewhereandorderBysemantics 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 makesliveQueryrefresh. offlineenables offline plugin generation and sync config wiring.- Offline sync expects stable keys and usable
updatedAtcomparison values. offline.serializeDateValueexists for non-default date comparison serialization.- A create whose primary key already exists returns
409, so a replayed create is dropped from the offline queue instead of being retried forever. The check runs only after the insert fails and covers primary keys only, not other unique indexes. - Failed operations inside a
_batchrequest carry their HTTP status back to the client, socreateErrorin a hook behaves the same batched and unbatched.
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. *.beforehooks can calltransformQuery(({ where, extras }) => ...)to add constraints before execution.- Use
allowTables([...])to deny access to unlisted generated collections. - Use the
realtime.filterhook to reject websocket updates for a peer when row-level rules apply, orpayload.narrowRecord({ ... })to deliver only a column subset to that peer (keep the primary key;fieldTimestampsis narrowed for you).
Guardrails
- If drizzle config is missing, module setup is skipped after warning.
- Non-string
schemain 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.whereis deprecated; usefindOptions.where.allowTablesflips 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 toallowTables— otherwise endpoints throwCollection "<name>" is not allowed.at runtime.- Do not add a pre-insert existence check in an
index.post.beforehook to guard against duplicate creates — the module already answers a duplicate primary key with409, and the extraSELECTcosts a round-trip on every create while still racing concurrent inserts. - Do not hand-write
server/api/<table>/*CRUD routes for tables already exposed by the generatedapiPath. Duplicate code paths drift, bypassallowTables/hooksForTable, and miss realtime publishing — extend behavior through hooks or usepublishRstoreDrizzleRealtimeUpdatefrom a justified custom route.
References
| Topic | Description | Reference | |
|---|---|---|---|
| API index | Full map of Nuxt-Drizzle API/config references | api-index | |
| rstoreDrizzle.drizzleConfigPath | Drizzle config lookup path | api-drizzle-config-path | |
| rstoreDrizzle.drizzleImport | Server drizzle getter import contract | api-drizzle-import | |
| rstoreDrizzle.apiPath | Generated REST base path | api-api-path | |
| rstoreDrizzle.ws | Enable websocket realtime integration | api-ws | |
| rstoreDrizzle.ws.apiPath | Override websocket endpoint path | api-ws-api-path | |
| rstoreDrizzle.offline | Enable offline sync integration | api-offline | |
| rstoreDrizzle.offline.serializeDateValue | Customize offline sync date serialization | api-offline-serialize-date-value | |
| findOptions.include | Primary relation include option | api-find-options-include | |
| findOptions.where | Primary drizzle filter option | api-find-options-where | |
| params.where (deprecated) | Legacy filter location | api-params-where | |
| params.limit | Limit rows in list queries | api-params-limit | |
| params.offset | Offset rows in list queries | api-params-offset | |
| params.with | Low-level Drizzle relation override | api-params-with | |
| params.columns | Selected column projection | api-params-columns | |
| params.orderBy | Sort order format and behavior | api-params-order-by | |
| params.keys | Key-constrained list fetches | api-params-keys | |
| filterWhere | Local cache condition evaluator | api-filter-where | |
| rstoreDrizzleHooks | Global server/realtime hook bus | api-rstore-drizzle-hooks | |
| hooksForTable | Table-scoped hook registration helper | api-hooks-for-table | |
| allowTables | Collection allow-list access control | api-allow-tables | |
| publishRstoreDrizzleRealtimeUpdate | Publish manual realtime updates for direct Drizzle writes | api-publish-rstore-drizzle-realtime-update | |
| Base @rstore/nuxt skill | Nuxt module/runtime integration semantics | rstore-nuxt skill | |
| Base @rstore/vue skill | Underlying collection/query/form semantics | rstore-vue skill |
Further reading
- Nuxt + Drizzle docs: https://rstore.akryum.dev/plugins/nuxt-drizzle
- Query docs: https://rstore.akryum.dev/guide/data/query
- Live docs: https://rstore.akryum.dev/guide/data/live
- Offline docs: https://rstore.akryum.dev/guide/data/offline
- @rstore/nuxt skill:
rstore-nuxt - @rstore/vue skill:
rstore-vue
Security
- Security scanPassed, 96 of 100Pattern checks on the skill's files.
- Model reviewNot runA model review is scheduled for the next rescan.
Quality evaluation
- Task evaluationNot runNo evaluation yet. Run one in Skill Studio.
Source
- SKILL.md in the repositoryCheckingpackages/nuxt-drizzle/skills/rstore-nuxt-drizzle/SKILL.md
Popularity
- Repository stars
- 413
- Repository forks
- 17
- 7-day trend score
- 30
Stars and forks are for the whole GitHub repository, not this skill alone. Refreshed 19 Mar 2026, 7 months ago.
Works with
Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.
- Claude Code
- Cursor
- GitHub Copilot
- Windsurf
- Codex
- Gemini CLI
- Cline
- Amp
- Roo Code
- Goose
- Continue
- Kilo Code
- Junie
- Trae
- OpenHands
- Qwen Code
- AI Agent
- Tabnine Chat
- Sourcegraph Cody
- Amazon Q
- Firebase Genkit
- Sweep
- Devin
- Phind
- Mentat
- Claude Desktop
- Aider
- GPT Pilot
- Plandex
- bolt.new
- v0
- Replit Agent
- Anse
- TypingMind
- BetterChatGPT
- LibreChat
- Warp AI
- Pieces
- Fig AI
Embed badge
[](https://verified-skill.com/skills/directus/rstore/rstore-nuxt-drizzle)