Claude Code for Xata Database — Workflow Guide

Written by Michael Lip · Solo founder of Zovo · $400K+ on Upwork · 100% JSS Join 50+ builders · More at zovo.one

The Setup

You are building with Xata, the serverless database platform that combines PostgreSQL with built-in full-text search, vector search, file attachments, and a REST API. Xata generates a type-safe TypeScript client from your schema. Claude Code can write Xata queries, but it defaults to raw SQL or Prisma patterns instead of the Xata SDK.

What Claude Code Gets Wrong By Default

  1. Writes raw PostgreSQL queries. Claude generates SELECT * FROM users WHERE id = $1. Xata has its own SDK with a chainable query API: xata.db.users.filter({ id }).getFirst(). Raw SQL is available but not the primary interface.

  2. Uses Prisma for database access. Claude sets up Prisma with connection strings. Xata generates its own typed client with xata codegen — no Prisma needed. The client is generated from your Xata schema.

  3. Creates migration files manually. Claude writes SQL migrations. Xata uses branch-based schema changes through the dashboard or CLI — xata schema edit or push changes from a branch, not file-based migrations.

  4. Ignores built-in search and vector capabilities. Claude adds Algolia for search or Pinecone for vectors. Xata has built-in full-text search (xata.db.posts.search('query')) and vector search (vectorSearch) at no extra cost.

The CLAUDE.md Configuration

# Xata Database Project

## Database
- Platform: Xata (serverless Postgres + search + vectors)
- Client: auto-generated TypeScript SDK (xata codegen)
- Schema: managed via Xata CLI or dashboard
- Branches: database branching for dev/preview

## Xata Rules
- Generate client: npx xata codegen
- Query: xata.db.tableName.filter({...}).getMany()
- Create: xata.db.tableName.create({ ... })
- Update: xata.db.tableName.update(id, { ... })
- Search: xata.db.tableName.search('query', { fuzziness: 1 })
- Vector: xata.db.tableName.vectorSearch('column', vector, { size: 10 })
- File attachments: built-in file column type
- Client initialized in lib/xata.ts (auto-generated)

## Conventions
- Generated client in src/xata.ts (regenerate after schema changes)
- XATA_BRANCH env var for database branch selection
- Main branch for production, feature branches for dev
- Schema changes via xata schema edit or dashboard
- Full-text search on text columns (enabled in schema)
- Never write raw SQL unless using Xata's SQL proxy endpoint

Workflow Example

You want to add a searchable blog with Xata’s built-in search. Prompt Claude Code:

“Create a blog posts table in Xata with title, content, author, tags, and publishedAt fields. Enable full-text search on title and content. Write functions to create posts, list posts with pagination, and search posts with fuzzy matching.”

Claude Code should define the schema via Xata CLI, run xata codegen to generate the client, then write functions using xata.db.posts.create(), xata.db.posts.getMany({ pagination: { size: 10, offset } }), and xata.db.posts.search('query', { fuzziness: 1, target: ['title', 'content'] }).

Common Pitfalls

  1. Forgetting to regenerate the client. Claude adds columns to the schema but does not run xata codegen. The TypeScript client becomes stale and new columns are not available with type safety. Always regenerate after schema changes.

  2. Branch confusion in deployments. Claude hardcodes XATA_BRANCH=main everywhere. Vercel preview deployments should use a matching Xata branch. Configure the Xata-Vercel integration to automatically create database branches for preview deploys.

  3. Search index lag on new records. Claude creates a record and immediately searches for it. Xata’s search index updates asynchronously — newly created records may not appear in search results for a few seconds. Use direct queries for immediate reads after writes.