A LangGraph checkpoint saver implementation using Bun's native SQLite
This package requires Bun runtime and will NOT work with Node.js, Deno, or other JavaScript runtimes.
It uses Bun's native bun:sqlite module which is not available in other runtimes. If you need SQLite checkpoint storage for Node.js, please use @langchain/langgraph-checkpoint-sqlite instead.
langgraph-checkpoint-bunsqlite provides persistent checkpoint storage for LangGraph.js applications using Bun's native bun:sqlite module. This implementation offers:
- 🚀 Native Performance - Uses Bun's built-in SQLite for maximum speed
- 💾 Persistent Storage - Save checkpoints to disk or use in-memory database
- 🔒 Type Safety - Full TypeScript support with strict type checking
- 🧹 Clean API - Simple, intuitive interface matching LangGraph standards
- 📦 Zero Config - Works out of the box with sensible defaults
- 🔄 API Compatible - Aligned with
@langchain/langgraph-checkpoint-sqlitefor easy migration
Prerequisites: You must have Bun installed. This package does not work with Node.js.
bun add https://github.com/tarnishablec/langgraph-checkpoint-bunsqlite.git
⚠️ Runtime Requirement: This package ONLY works with Bun runtime. It usesbun:sqlitewhich is not available in Node.js, Deno, or other JavaScript runtimes. Attempting to use it with other runtimes will result in module resolution errors.
import { BunSqliteSaver } from "langgraph-checkpoint-bunsqlite";
// Create an in-memory checkpoint saver (great for development/testing)
const saver = new BunSqliteSaver();
// Or use the fromConnString factory method (API compatible with @langchain/langgraph-checkpoint-sqlite)
const saverFromConn = BunSqliteSaver.fromConnString(":memory:");
// Or use a file-based database for persistence
const persistentSaver = new BunSqliteSaver({
dbPath: "./my-checkpoints.db"
});
// Or with fromConnString
const persistentSaverFromConn = BunSqliteSaver.fromConnString("./my-checkpoints.db");import { StateGraph } from "@langchain/langgraph";
import { BunSqliteSaver } from "langgraph-checkpoint-bunsqlite";
// Define your state type
interface AgentState {
messages: string[];
count: number;
}
// Create checkpoint saver
const checkpointer = new BunSqliteSaver({
dbPath: "./agent-checkpoints.db"
});
// Create a graph with checkpointing
const graph = new StateGraph<AgentState>({
channels: {
messages: {
value: (prev: string[], next: string[]) => [...prev, ...next],
default: () => [],
},
count: {
value: (prev: number, next: number) => next,
default: () => 0,
},
},
})
.addNode("agent", async (state) => {
return {
messages: [`Step ${state.count}`],
count: state.count + 1,
};
})
.addEdge("__start__", "agent")
.addEdge("agent", "__end__")
.compile({ checkpointer });
// Run with checkpointing
const threadId = "thread-1";
const config = { configurable: { thread_id: threadId } };
// First run
await graph.invoke(
{ messages: [], count: 0 },
config
);
// Resume from checkpoint
const result = await graph.invoke(
{ messages: [], count: 0 },
config
);
// Clean up
checkpointer.close();This package provides API compatibility with @langchain/langgraph-checkpoint-sqlite through the fromConnString factory method and get() method, making it easy to switch between implementations.
interface BunSqliteSaverConfig {
/**
* Path to the SQLite database file.
* Use ":memory:" for an in-memory database (default).
*/
dbPath?: string;
/**
* Custom serializer for checkpoint data.
* If not provided, uses the default serializer from BaseCheckpointSaver.
*/
serializer?: SerializerProtocol;
}// In-memory database
const saver = BunSqliteSaver.fromConnString(":memory:");
// File-based database
const fileSaver = BunSqliteSaver.fromConnString("./checkpoints.db");
// With custom serializer
import { JsonPlusSerializer } from "@langchain/langgraph-checkpoint";
const customSaver = BunSqliteSaver.fromConnString(
"./checkpoints.db",
new JsonPlusSerializer()
);const saver1 = new BunSqliteSaver();
// or explicitly
const saver2 = new BunSqliteSaver({ dbPath: ":memory:" });const saver = new BunSqliteSaver({
dbPath: "./checkpoints.db"
});import { BunSqliteSaver } from "langgraph-checkpoint-bunsqlite";
import { JsonPlusSerializer } from "@langchain/langgraph-checkpoint";
const saver = new BunSqliteSaver({
dbPath: "./checkpoints.db",
serializer: new JsonPlusSerializer()
});import { Database } from "bun:sqlite";
import { BunSqliteSaver } from "langgraph-checkpoint-bunsqlite";
const db = new Database("./my-app.db");
const saver = BunSqliteSaver.fromDatabase(db);
// The saver will use your existing database
// Note: calling saver.close() will NOT close the database
// since it's externally ownedThe main class implementing checkpoint persistence.
new BunSqliteSaver(config?: BunSqliteSaverConfig)
Create a saver from a connection string. This method provides API compatibility with @langchain/langgraph-checkpoint-sqlite.
Parameters:
connString: Path to the SQLite database file (use:memory:for in-memory database)serde: Optional custom serializer
// Create an in-memory saver
const saver = BunSqliteSaver.fromConnString(":memory:");
// Create a file-based saver
const fileSaver = BunSqliteSaver.fromConnString("./checkpoints.db");Create a saver from an existing Database instance. The database will not be closed when close() is called.
Get just the checkpoint without the full tuple. This provides a simpler API for users who just need the checkpoint data.
const checkpoint = await saver.get({
configurable: {
thread_id: "thread-1",
checkpoint_id: "checkpoint-123"
}
});
// Or get the latest checkpoint
const latestCheckpoint = await saver.get({
configurable: { thread_id: "thread-1" }
});Retrieve a specific checkpoint tuple.
const tuple = await saver.getTuple({
configurable: {
thread_id: "thread-1",
checkpoint_id: "checkpoint-123"
}
});List checkpoints for a thread.
const checkpoints = saver.list(
{ configurable: { thread_id: "thread-1" } },
{ limit: 10 }
);
for await (const checkpoint of checkpoints) {
console.log(checkpoint.checkpoint.id);
}put(config: RunnableConfig, checkpoint: Checkpoint, metadata: CheckpointMetadata): Promise<RunnableConfig>
Save a checkpoint.
const newConfig = await saver.put(config, checkpoint, metadata);Save pending writes for a checkpoint.
await saver.putWrites(config, writes, "task-1");Delete all checkpoints and writes for a thread.
await saver.deleteThread("thread-1");
console.log("Thread deleted");Delete a specific checkpoint. Returns true if deleted, false if not found.
const deleted = saver.deleteCheckpoint("thread-1", "checkpoint-123");Get statistics about the stored data.
const stats = saver.getStats();
console.log(`Total checkpoints: ${stats.totalCheckpoints}`);
console.log(`Total writes: ${stats.totalWrites}`);
console.log(`Total threads: ${stats.totalThreads}`);Close the database connection. Should be called when done with the saver.
saver.close();The implementation uses two tables:
Stores checkpoint data.
| Column | Type | Description |
|---|---|---|
| thread_id | TEXT | Thread identifier |
| checkpoint_ns | TEXT | Checkpoint namespace (default: "") |
| checkpoint_id | TEXT | Checkpoint identifier |
| parent_checkpoint_id | TEXT | Parent checkpoint ID (nullable) |
| type | TEXT | Serialization type |
| checkpoint | BLOB | Serialized checkpoint data |
| metadata | BLOB | Serialized metadata |
Primary Key: (thread_id, checkpoint_ns, checkpoint_id)
Stores pending writes associated with checkpoints.
| Column | Type | Description |
|---|---|---|
| thread_id | TEXT | Thread identifier |
| checkpoint_ns | TEXT | Checkpoint namespace |
| checkpoint_id | TEXT | Checkpoint identifier |
| task_id | TEXT | Task identifier |
| idx | INTEGER | Write index |
| channel | TEXT | Channel name |
| type | TEXT | Serialization type |
| value | BLOB | Serialized value |
Primary Key: (thread_id, checkpoint_ns, checkpoint_id, task_id, idx)
This package is built with strict TypeScript settings and requires proper configuration in your project:
{
"compilerOptions": {
"strict": true,
"alwaysStrict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"types": ["bun-types"]
}
}bun run typecheck.
├── index.ts # Main implementation
├── package.json # Package configuration
├── tsconfig.json # TypeScript configuration
└── README.md # Documentation
This package provides multiple API patterns that are interchangeable:
// Pattern 1: Using fromConnString and get() (compatible with @langchain/langgraph-checkpoint-sqlite)
const saver1 = BunSqliteSaver.fromConnString(":memory:");
const checkpoint = await saver.get(config);
// Pattern 2: Using constructor and getTuple() (full tuple with metadata)
const saver2 = new BunSqliteSaver({ dbPath: ":memory:" });
const tuple = await saver.getTuple(config);
// Both patterns are valid and can be used based on your preferenceconst saver = new BunSqliteSaver({ dbPath: "./checkpoints.db" });
try {
// Use saver
} finally {
saver.close();
}const userId = "user-123";
const sessionId = "session-456";
const threadId = `${userId}:${sessionId}`;
const config = { configurable: { thread_id: threadId } };// Clean up old threads periodically
const oldThreads = await findOldThreads();
for (const threadId of oldThreads) {
await saver.deleteThread(threadId);
}// Check storage stats regularly
const stats = saver.getStats();
if (stats.totalCheckpoints > 10000) {
console.warn("High checkpoint count, consider cleanup");
}The saver will throw errors in the following cases:
- Missing required
thread_idin config - Missing required
checkpoint_idwhen callingputWrites - Database connection errors
- Serialization/deserialization errors
Always wrap operations in try-catch blocks:
try {
await saver.put(config, checkpoint, metadata);
} catch (error) {
console.error("Failed to save checkpoint:", error);
// Handle error appropriately
}- Indexes: The schema includes indexes on frequently queried columns
- Batch Operations: Use transactions for multiple operations (future enhancement)
- In-Memory: Use
:memory:for development and testing - File-Based: Use file-based databases for production persistence
- Bun Only: This package requires Bun runtime and will not work with Node.js
- SQLite Limitations: Subject to SQLite's limitations (file locking, etc.)
- No Migrations: Schema changes require manual migration
Contributions are welcome! Please ensure:
- All code passes
bun run typecheck - No
anytypes are used - Code follows existing style
- Documentation is updated
MPL-2.0
- LangGraph.js - The main LangGraph framework
- @langchain/langgraph-checkpoint - Base checkpoint interfaces
- Bun - The JavaScript runtime
For issues and questions:
- GitHub Issues: Report a bug
- Documentation: This README
- LangGraph Docs: LangGraph Documentation