CLAUDE LABJP
FORK — Claude Code 2.1.212 changes what /fork does: it copies your conversation into a new background session with its own row in claude agents, so you can keep working. The old in-session subagent is now /subtaskLIMITS — WebSearch calls are now capped at 200 per session by default, and subagent spawns get the same 200 ceiling, so a runaway search or delegation loop stops on its ownMCPBG — MCP tool calls running past two minutes now move to the background automatically, keeping the session usable. Tune the threshold with CLAUDE_CODE_MCP_AUTO_BACKGROUND_MSPLANFIX — Fixed plan mode auto-running file-modifying Bash commands such as touch and rm without a permission prompt or an SDK canUseTool callbackSONNET5 — Claude Sonnet 5 is running on introductory pricing of $2 per million input tokens and $10 per million output. After August 31 it moves to $3 and $15IPO — Bankers are reportedly lining up investor meetings for Anthropic ahead of a possible public listing as soon as OctoberFORK — Claude Code 2.1.212 changes what /fork does: it copies your conversation into a new background session with its own row in claude agents, so you can keep working. The old in-session subagent is now /subtaskLIMITS — WebSearch calls are now capped at 200 per session by default, and subagent spawns get the same 200 ceiling, so a runaway search or delegation loop stops on its ownMCPBG — MCP tool calls running past two minutes now move to the background automatically, keeping the session usable. Tune the threshold with CLAUDE_CODE_MCP_AUTO_BACKGROUND_MSPLANFIX — Fixed plan mode auto-running file-modifying Bash commands such as touch and rm without a permission prompt or an SDK canUseTool callbackSONNET5 — Claude Sonnet 5 is running on introductory pricing of $2 per million input tokens and $10 per million output. After August 31 it moves to $3 and $15IPO — Bankers are reportedly lining up investor meetings for Anthropic ahead of a possible public listing as soon as October
Articles/API & SDK
API & SDK/2026-04-25Advanced

Claude API × Convex: Reactive AI Apps — Data Flow, Streaming, and Agent Patterns

How to combine Convex's reactive database with the Claude API to build chat and agent applications that hold up in production. Covers schema design, the Action/Mutation/Query boundary, streaming, tool-call state, and the cold-start pitfalls nobody warns you about.

Claude API115ConvexAgent SDK4TypeScript24RealtimeProduction23

Premium Article

Over the last few weeks I've gotten surprisingly similar messages from people building on Convex and Claude together. "Multiplayer chat feels instant, but when I call Claude the latency doubles." "My long-running tool use gets killed by the Action timeout." "A browser refresh wipes out the half-finished tool output I was streaming." These symptoms show up when Convex's reactive worldview is squeezed into the same layer as Claude's async, streaming-first calls. The two models don't blend — they need to be composed.

This article walks through the design I use in production to make Convex and the Claude API sit well together: how to split the layers, how to shape your schema, how to sync streams without breaking the bank on writes, how to manage tool execution, and the cold-start trap most guides skip. Code is TypeScript with recent stable versions of @anthropic-ai/sdk and convex (April 2026), but the principles should survive version drift.

When Convex is the right foundation for an AI app

Convex is a serverless document database with reactive queries at its core. It's not just a database — subscribed queries automatically propagate deltas to clients on every write. That property is unusually well matched to AI chat, because you no longer have to hand-roll the "append message, notify listeners, re-render" dance. Your UI just subscribes to the messages table and everything downstream becomes declarative.

That said, Convex has serverless constraints you can't pretend away. Mutations and queries have strict CPU budgets, and external API calls must go through Actions. A Claude call that takes 5 to 60 seconds always lives in an Action, and the result gets written back via ctx.runMutation(). If you catch yourself wanting to "just fetch from a Mutation," let the urge pass. Crossing that boundary now saves a painful refactor later.

I reach for Convex when the client side needs real-time sync and there's a collaborative angle — multiplayer editing, shared dashboards, co-authored artifacts. For a plain single-user chat UI sitting in front of an existing Postgres-and-GraphQL stack, pulling in Convex isn't worth the operational surface area. A direct SSE from a Node or Cloudflare Worker, like the pattern in the production streaming chat guide, will have lower tail latency.

Schema design — the three-table model for threads, messages, and tool calls

The schema I use in production is shaped around two use cases at once: interactive chat and autonomous agents. Even at its smallest it separates threads, messages, and tool executions. That separation is what makes the "track in-progress work as its own record" pattern work later.

// convex/schema.ts
import { defineSchema, defineTable } from "convex/server";
import { v } from "convex/values";
 
export default defineSchema({
  threads: defineTable({
    userId: v.string(),
    title: v.string(),
    model: v.string(), // e.g. "claude-sonnet-4-6"
    systemPrompt: v.optional(v.string()),
    totalInputTokens: v.number(),
    totalOutputTokens: v.number(),
    archivedAt: v.optional(v.number()),
  }).index("byUser", ["userId", "archivedAt"]),
 
  messages: defineTable({
    threadId: v.id("threads"),
    role: v.union(v.literal("user"), v.literal("assistant"), v.literal("tool")),
    // While streaming, keep appending to contentDraft; promote to content on finish
    content: v.optional(v.string()),
    contentDraft: v.optional(v.string()),
    status: v.union(
      v.literal("pending"),
      v.literal("streaming"),
      v.literal("complete"),
      v.literal("error"),
    ),
    errorMessage: v.optional(v.string()),
    inputTokens: v.optional(v.number()),
    outputTokens: v.optional(v.number()),
  }).index("byThread", ["threadId"]),
 
  toolCalls: defineTable({
    messageId: v.id("messages"),
    name: v.string(),
    input: v.any(),
    output: v.optional(v.any()),
    status: v.union(
      v.literal("pending"),
      v.literal("running"),
      v.literal("success"),
      v.literal("failure"),
    ),
    startedAt: v.number(),
    completedAt: v.optional(v.number()),
  }).index("byMessage", ["messageId"]),
});

The reason contentDraft and content are separate fields is so that "work-in-progress" and "final" never share a slot. If your main UI subscribes to content only, you eliminate a class of flicker bugs where a partially streamed message briefly looks final. When you want to show a typing cursor, a side component can subscribe to contentDraft instead. That split is small but pays dividends.

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
If you started combining Convex and Claude and got stuck on where Action ends and Mutation begins, you'll have a concrete boundary design to copy
You'll learn how to run long-lived agents on serverless infrastructure using scheduled steps and progress syncing, without timeouts biting you
You'll walk away with production-ready patterns for cost tracking, rate limiting, and schema evolution that you can paste into your own project
Secure payment via Stripe · Cancel anytime

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Claude Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

Related Articles

API & SDK2026-07-01
When Claude API Document Extraction Is Confidently Wrong — Field Notes on Catching Silent Errors with Invariants
In structured extraction from invoices and contracts, the real danger isn't a crash — it's a value that's silently wrong while the schema validates and confidence reads high. Field notes on invariants, two-pass extraction, and tracking field-level error rates.
API & SDK2026-05-04
Claude API on Bun in Production: Migration Decisions and Implementation Patterns That Actually Survive Real Traffic
A practical guide to running Claude API services on Bun in production. Covers migration triggers from Node.js, built-in SQLite/WebSocket usage, streaming optimization, and the pitfalls that only surface after deployment — with working code and measured numbers.
API & SDK2026-07-09
When the RAG Started Being Confidently Wrong — Field Notes on Measuring Retrieval Misses With Groundedness
In a Claude API RAG, the answers stay fluent while the facts drift. Often the cause is a silent recall decay on the retrieval side, missing the document that holds the answer. Field notes on measuring groundedness and retrieval hit rate and walking the system back, with working code and real numbers.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →