ai-persistence/build-drizzle-adapter

Generates a Drizzle ORM chat persistence layer with four tables and idempotent store implementations.

3.1k|316|Updated Oct 8, 2025
One-click install
npx skills add https://github.com/TanStack/ai --skill ai-persistence-build-drizzle-adapter
Or copy as Structured Prompt for Agent
Please help me install this Agent Skill.
Skill: ai-persistence/build-drizzle-adapter
Source: https://github.com/TanStack/ai/tree/main/packages/ai-persistence/skills/ai-persistence/build-drizzle-adapter
Command: npx skills add https://github.com/TanStack/ai --skill ai-persistence-build-drizzle-adapter

SYSTEM DOCUMENTATION & REQUIREMENTS

💡 This Skill requires @tanstack/ai, @tanstack/ai-persistence, drizzle-orm, drizzle-kit.

What problem does it solve?

Apps already running Drizzle ORM need durable TanStack AI chat state (threads, runs, interrupts, metadata) without creating a second database connection, a parallel migration system, or a separate package. This Skill writes a single chat-persistence.ts file against the app's existing db handle, schema file, and drizzle-kit journal.

Core Features & Use Cases

  • Schema Integration: Appends four tables (chat_threads, chat_runs, chat_interrupts, chat_metadata) to the app's existing schema file for SQLite, Postgres, or MySQL dialects, matching existing naming conventions.
  • Idempotent Store Implementations: Implements MessageStore, RunStore, InterruptStore, and MetadataStore with onConflict rules, NULL-clearing semantics for detached runs, and insert-if-absent interrupt creation that pass the shared conformance testkit.
  • Per-Request Bindings: Supports request-scoped database handles like Cloudflare D1 by exporting a factory function instead of a module-level constant.
  • Use Case: A team with a Postgres-backed Drizzle app adds resumable AI chat: the Skill inspects their drizzle.config.ts, appends the tables, generates chat-persistence.ts, wires withPersistence into the chat route, and verifies with runPersistenceConformance.

Quick Start

Add TanStack AI chat persistence to my existing Drizzle app by generating the four tables and the chat-persistence.ts store file against my current db setup.

Frequently Asked Questions about ai-persistence/build-drizzle-adapter

High-intent search queries and answers about installing and using this skill.

FAQPage Schema
How do I add TanStack AI chat persistence to an existing Drizzle app?

Add four tables (chat_threads, chat_runs, chat_interrupts, chat_metadata) to your existing schema file, then create a chat-persistence.ts exporting a ChatPersistence built with defineAIPersistence from your existing db handle. Run your own drizzle-kit generate and migrate commands to apply the migration.

How do I implement idempotent run stores with Drizzle ORM?

Use onConflictDoNothing on runId inserts for createOrResume, then re-read the row so a concurrent writer wins. For updates, use 'field' in patch checks rather than undefined comparisons so explicit clears of detachedSince and cancelRequested write NULL correctly.

Does the Drizzle chat persistence adapter work with Cloudflare D1?

Yes, but D1 bindings are request-scoped, so export a factory function that calls getDb() inside the handler instead of a module-level constant. The store factory functions stay identical; only the export shape changes.

Which Drizzle dialects are supported for chat persistence tables?

SQLite, Postgres, and MySQL are supported. SQLite uses text with json mode, Postgres uses jsonb and bigint timestamps, and MySQL uses json with varchar primary keys and onDuplicateKeyUpdate instead of onConflictDoUpdate.

Why does storing null in a Drizzle JSON metadata column fail?

A JSON-mode column binds JavaScript null as SQL NULL, which a NOT NULL column rejects with an opaque driver error. The metadata store throws a clear TypeError instead and directs callers to use delete() to clear values.

How do I verify a custom Drizzle persistence adapter is correct?

Run runPersistenceConformance from @tanstack/ai-persistence/testkit against a throwaway database with the migration applied. Declare omitted stores like generationRuns with skip, and unimplemented optional methods with skipMethods.