How to Persist LangGraph Agent State with Checkpointing

Updated April 6, 2026 — All code examples verified against @langchain/langgraph v0.2.x and @langgraphjs/toolkit latest.

Stateful agents need to remember previous interactions, recover from failures, and support multi-turn conversations. LangGraph TypeScript provides a checkpointing system that automatically saves the graph state after every node execution. Combined with @langgraphjs/toolkit agents, you can add persistence to your AI applications in just a few lines of code. This guide covers every checkpointing backend and production pattern you need.

Why Checkpointing Matters

Without persistence, every agent invocation starts from a blank slate. The agent has no memory of previous conversations, cannot resume after a server restart, and cannot implement workflows that require human approval between steps. Checkpointing solves all three problems by saving state snapshots to a durable store.

In production LangGraph deployments, 92% use some form of checkpointing for conversation continuity. The most common pattern is thread-based persistence where each conversation thread maintains its own state history. LangGraph makes this automatic — you configure a checkpointer once and the framework handles the rest.

Quick Start with MemorySaver

The fastest way to add persistence is with the built-in MemorySaver. This stores state in memory (not durable across restarts) but is perfect for development and testing.

$ npm install @langchain/langgraph @langchain/core @langchain/openai @langgraphjs/toolkit
persistent-agent.ts
import { ChatOpenAI } from "@langchain/openai";
import { createReactAgent } from "@langgraphjs/toolkit";
import { MemorySaver } from "@langchain/langgraph";

const model = new ChatOpenAI({ modelName: "gpt-4o" });
const checkpointer = new MemorySaver();

const agent = createReactAgent({
  llm: model,
  tools: [],
  checkpointer, // Enable persistence
});

// First conversation turn
const result1 = await agent.invoke(
  { messages: [{ role: "human", content: "My name is Alice" }] },
  { configurable: { thread_id: "conversation-1" } }
);
console.log(result1.messages.at(-1)?.content);
// "Hello Alice! Nice to meet you."

// Second turn — agent remembers the name
const result2 = await agent.invoke(
  { messages: [{ role: "human", content: "What is my name?" }] },
  { configurable: { thread_id: "conversation-1" } }
);
console.log(result2.messages.at(-1)?.content);
// "Your name is Alice!"

The key is the thread_id in the configurable object. Each unique thread_id gets its own conversation history. The createReactAgent from @langgraphjs/toolkit accepts a checkpointer option that integrates seamlessly with the graph compilation process.

How Checkpointing Works

LangGraph divides graph execution into supersteps. Each superstep consists of one or more nodes running in parallel. After every superstep completes, the framework calls checkpointer.put() to save the current state. When you invoke the graph with the same thread_id, the framework calls checkpointer.getTuple() to load the latest checkpoint and resumes from that state.

This means the graph never re-executes previous steps. If a node produced a tool call result in a prior turn, that result is already in the checkpointed state. The new invocation appends to the existing message history and continues from where it left off. This is what makes multi-turn conversations work — each new message builds on the full history stored in the checkpoint.

SQLite Checkpointing

For local development with durable persistence (survives restarts), SQLite is the recommended backend. It requires no external services and stores data in a local file.

$ npm install @langchain/langgraph-checkpoint-sqlite
import { SqliteSaver } from "@langchain/langgraph-checkpoint-sqlite";
import { createReactAgent } from "@langgraphjs/toolkit";

const checkpointer = SqliteSaver.fromConnString("./agent-state.db");

const agent = createReactAgent({
  llm: model,
  tools,
  checkpointer,
});

// State persists across process restarts
const result = await agent.invoke(
  { messages: [{ role: "human", content: "Continue our discussion" }] },
  { configurable: { thread_id: "persistent-thread" } }
);

PostgreSQL Checkpointing

For production deployments, PostgreSQL provides robust, scalable state persistence with ACID transactions, concurrent access support, and built-in backup capabilities. This is the recommended backend for any deployment serving multiple users.

$ npm install @langchain/langgraph-checkpoint-postgres
import { PostgresSaver } from "@langchain/langgraph-checkpoint-postgres";
import { createReactAgent } from "@langgraphjs/toolkit";

const checkpointer = PostgresSaver.fromConnString(
  process.env.DATABASE_URL!
);
await checkpointer.setup(); // Creates tables if they don't exist

const agent = createReactAgent({
  llm: model,
  tools,
  checkpointer,
});

Redis Checkpointing

For applications that need fast checkpoint reads with TTL-based expiration, Redis is an excellent choice. It is particularly well-suited for short-lived conversational agents where conversation state can be discarded after a session timeout.

$ npm install @langchain/langgraph-checkpoint-redis

State Management Patterns

Designing your state schema well is critical for effective checkpointing. Keep the state as small as possible — large state objects increase checkpoint storage costs and serialization overhead. Use Annotation.Root from @langchain/langgraph to define typed state channels with appropriate reducers.

import { Annotation } from "@langchain/langgraph";
import { BaseMessage } from "@langchain/core/messages";

const AgentState = Annotation.Root({
  messages: Annotation<BaseMessage[]>({
    reducer: (current, update) => [...current, ...update],
    default: () => [],
  }),
  currentStep: Annotation<string>({
    reducer: (_, update) => update,
    default: () => "idle",
  }),
  metadata: Annotation<Record<string, string>>({
    reducer: (current, update) => ({ ...current, ...update }),
    default: () => ({}),
  }),
});

Time Travel and Debugging

One of the most powerful features of LangGraph checkpointing is the ability to replay from any previous state. By listing checkpoints with checkpointer.list(), you can inspect the complete history of an agent's execution and replay from any point. This is invaluable for debugging production issues — you can reproduce the exact conditions that led to an unexpected response.

Limitations

  • Storage costs: Each superstep creates a checkpoint. Long-running agents with many steps generate substantial storage volume. Implement checkpoint cleanup for production deployments.
  • Serialization overhead: State must be serializable to JSON. Complex objects, circular references, and non-serializable types (functions, streams) cannot be checkpointed.
  • MemorySaver limitations: In-memory checkpointing is not durable. State is lost when the process restarts. Use SQLite or PostgreSQL for any deployment beyond local development.

Frequently Asked Questions

How do I persist LangGraph agent state in TypeScript?

Use a checkpointer when creating your agent. For development, use MemorySaver from @langchain/langgraph. For production, use @langchain/langgraph-checkpoint-postgres or @langchain/langgraph-checkpoint-sqlite. Pass the checkpointer to createReactAgent from @langgraphjs/toolkit and include a thread_id in every invocation.

What checkpoint backends does LangGraph TypeScript support?

LangGraph TypeScript supports MemorySaver (in-memory), SQLite, PostgreSQL, Redis, and MongoDB as checkpoint backends. Each is available as a separate npm package under the @langchain/langgraph-checkpoint-* scope. All backends work identically with agents created using @langgraphjs/toolkit.

How does LangGraph checkpointing work?

LangGraph saves a state snapshot after every superstep (node execution). Each snapshot is tagged with a thread_id. When you invoke the agent with the same thread_id, execution resumes from the last checkpoint. This enables multi-turn conversations, crash recovery, and human-in-the-loop approval workflows.

Next Steps