lukasa1993/tanstack-skills-extracted/tanstack-ai-persistence-build-drizzle-adapter
Claude Code and agent skill by lukasa1993/tanstack-skills-extracted
- community
Checking that the SKILL.md file is still in the repository.
Use when an app already runs Drizzle ORM and needs TanStack AI chat persistence — writes a chat-persistence.ts into the app against its existing db handle, schema file, and drizzle-kit journal. Covers the four tables (SQLite/Postgres/MySQL), the onConflict idempotency rules, JSON columns, and per-request bindings like D1.
Install
npx vskill@latest install lukasa1993/tanstack-skills-extracted/tanstack-ai-persistence-build-drizzle-adapterSKILL.md
Drizzle Chat Persistence
The deliverable is one file in the app — src/lib/chat-persistence.ts — exporting a ChatPersistence built from the app's existing Drizzle db. Plus four tables added to the app's existing schema file and a migration generated through the app's existing drizzle-kit setup.
Do not create a package, a second db instance, a migration runner, or a drizzle.config.ts. The app has those.
Always write all four tables and all four stores: threads, runs, interrupts, metadata. This is true on every dialect. The docs call interrupts and metadata optional for a hand-rolled adapter; this recipe is not that case. withPersistence needs all four to type-check as ChatPersistence, and the conformance suite in step 5 fails without them. Do not stop at chat_threads + chat_runs.
Read the Store Reference (docs/persistence/store-reference.md) for the store contracts and invariants, and ai-persistence/stores for the shape rules. Every store below mirrors the reference in-memory backend in @tanstack/ai-persistence (memory.ts); the shared conformance testkit is the proof.
1. Read the app before writing anything
| Find | Where to look | What it decides | |
|---|---|---|---|
| Dialect | drizzle.config.ts dialect:, or the drizzle-orm/*-core import | sqlite-core vs pg-core vs mysql-core column builders | |
| Schema file(s) | drizzle.config.ts schema: glob | Where the four tables go — append, never start a new file | |
The db handle | src/db/index.ts, src/db.ts, src/server/db.ts | Module singleton (export const db) vs factory (getDb()) | |
| Migration flow | drizzle.config.ts out:, the migrations/ or drizzle/ journal | Which generate/apply commands to tell the user to run | |
| Naming conventions | Existing tables in the schema file | Table prefix, var casing, snake_case column names | |
| Import alias | tsconfig.json paths | @/db, ~/db, #/db/index, or a relative path |
Match what is already there. If their tables are chat_*-prefixed and their vars are camelCase, so are yours. If they already have a messages table for something else, prefix — the store code reads database names off the table objects, so any name works.
Never invent a migration path. Add the tables to their schema file, then have them run their own commands (npx drizzle-kit generate then migrate/push, or wrangler d1 migrations apply for D1). A parallel migration table behind their back is how schemas drift.
2. Add the tables to their schema file
SQLite. JSON payloads use text({ mode: 'json' }) so Drizzle round-trips objects for you; timestamps are integer epoch ms.
<!-- fence: drizzle-sqlite-schema -->
updatedAt on threads is an app-owned extra, not part of any contract — the stores never read columns they do not know about, so add userId, tenant ids, or audit columns the same way (nullable or defaulted so inserts still succeed). The namespace column is the MetadataStore first argument; the stock SQL in the guide calls the same column scope.
RunRecord.error is a structured RunError ({ message: string, code?: string }), so it gets two columns rather than one JSON blob: error for the provider's prose and errorCode for the stable classification an operator filters and groups by. error and errorCode always move together in update, so a later code-less failure can never leave a stale code from an earlier one behind.
Postgres (drizzle-orm/pg-core). Same four tables. jsonb() for the JSON payloads, bigint({ mode: 'number' }) for epoch-ms timestamps, boolean() for cancelRequested, integer() for driverEpoch. Copy this block; do not translate the SQLite one by hand.
<!-- fence: drizzle-pg-schema -->
MySQL (drizzle-orm/mysql-core): take the Postgres block and swap pgTable for mysqlTable, jsonb() for json(), and text() for varchar(..., { length: 255 }) on the primary-key columns. bigint({ mode: 'number' }), boolean(), integer(), the indexes, and the composite primary key stay as they are.
The store bodies in step 3 are identical across all three dialects. Only onConflictDoUpdate becomes onDuplicateKeyUpdate on MySQL.
3. Write src/lib/chat-persistence.ts
The whole file. Idempotency is the entire game — the comments below mark the rules the conformance suite checks.
<!-- fence: drizzle-chat-persistence -->
This is the start of the file. Read the full SKILL.md on GitHub.
Security
- Security scanPassed, 95 of 100Pattern checks on the skill's files.
- Model reviewPassed
Quality evaluation
- Task evaluationNot runNo evaluation yet. Run one in Skill Studio.
Source
- SKILL.md in the repositoryCheckingskills/tanstack-ai-persistence-build-drizzle-adapter/SKILL.md
Popularity
- 7-day trend score
- 0
Not refreshed yet.
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/lukasa1993/tanstack-skills-extracted/tanstack-ai-persistence-build-drizzle-adapter)