npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@skroyc/langgraph-supabase-checkpointer

v2.2.1

Published

LangGraphJS checkpoint saver implementation for Supabase with UUID thread_id and Supabase Storage support

Readme

@skroyc/langgraph-supabase-checkpointer

npm version License: MIT TypeScript

A robust, production-ready LangGraphJS checkpoint saver implementation for Supabase. Version 2.1 adds support for the deleteThread lifecycle method, alongside existing features like UUID thread IDs and optimized Supabase Storage integration.

Database SetupQuick StartAPI ReferenceContributing

📋 Table of Contents

✨ Features

  • 🚀 Full LangGraphJS Compatibility: Implements the complete BaseCheckpointSaver interface.
  • 🗑️ Thread Deletion: Includes the deleteThread method for permanently removing all data associated with a thread.
  • 🔒 Secure & Multi-Tenant: Isolates data by user_id with Row Level Security (RLS) enabled by default.
  • ⚡ High Performance: Optimized for high-throughput applications with atomic PostgreSQL functions and efficient blob storage.
  • 🧩 TypeScript First: Full type safety and excellent IDE support.
  • ✅ Officially Validated: Passes the entire official LangGraphJS checkpoint validation test suite (710/710 tests).
  • 📦 Minimal Dependencies: Only requires peer dependencies for core functionality.
  • 🆔 UUID Thread IDs: V2 uses UUID format for thread identifiers providing better performance and scalability.
  • 💾 Supabase Storage Integration: Large blob data is stored in Supabase Storage for optimal performance and cost efficiency.

🚀 Installation

Prerequisites

  • Node.js 18+ or Bun 1.0+
  • A Supabase project with PostgreSQL 14+
  • Basic understanding of LangGraphJS and its checkpointing system.

1. Install Package

Install the package using your preferred package manager:

# Using npm
npm install @skroyc/langgraph-supabase-checkpointer

# Using Yarn
yarn add @skroyc/langgraph-supabase-checkpointer

# Using pnpm
pnpm add @skroyc/langgraph-supabase-checkpointer

# Using Bun
bun add @skroyc/langgraph-supabase-checkpointer

2. Install Peer Dependencies

This package requires the following peer dependencies:

# Using npm
npm install @langchain/langgraph-checkpoint @langchain/core @supabase/supabase-js

# Using Yarn
yarn add @langchain/langgraph-checkpoint @langchain/core @supabase/supabase-js

# Using pnpm
pnpm add @langchain/langgraph-checkpoint @langchain/core @supabase/supabase-js

# Using Bun
bun add @langchain/langgraph-checkpoint @langchain/core @supabase/supabase-js

3. Set Environment Variables

Create a .env file in your project root with the following variables:

# Required
SUPABASE_URL=your_supabase_url
SUPABASE_ANON_KEY=your_supabase_anon_key

# Optional: Only needed for admin operations
SUPABASE_SERVICE_ROLE_KEY=your_service_role_key

🗄️ Database Setup

To set up your database, you need to run the migrations.sql script included with this package. This script is idempotent, meaning it can be run multiple times without causing errors.

There are two recommended ways to run the migration:

Option 1: Supabase Dashboard

  1. Go to your Supabase Dashboard and select your project.
  2. Navigate to the SQL Editor.
  3. Open the migrations.sql file from this package, copy its contents, and paste them into a new query.
  4. Click Run.

Option 2: Supabase CLI (or psql)

If you use the Supabase CLI or have direct psql access to your database, you can run the file directly.

Using Supabase CLI:

  1. Create a new migration file in your Supabase project (e.g., supabase/migrations/<timestamp>_langgraph_checkpointer.sql).
  2. Copy the content of migrations.sql from this package into the new file.
  3. Run supabase db push to apply the migration.

Using psql:

# Ensure you have the migrations.sql file
psql -h your-db-host -U postgres -d postgres -f migrations.sql

Supabase Storage Configuration

The migrations.sql script automatically attempts to create the required checkpoints storage bucket and configure its security policies.

Note: The user running the migration needs storage admin privileges to perform these actions. If the script fails due to insufficient permissions, you will need to create the bucket and policy manually as described below.

  1. Create Storage Bucket: In your Supabase Dashboard, navigate to Storage and create a new bucket named checkpoints.

  2. Configure Bucket Policy: In the SQL Editor, run the following query to create the Row Level Security policy for the bucket. This ensures users can only access their own data.

    -- Storage bucket policy for checkpoints
    CREATE POLICY "Users can access their own checkpoint blobs" ON storage.objects
      FOR ALL USING (bucket_id = 'checkpoints' AND auth.uid()::text = (storage.foldername(name))[1]);

⚡ Quick Start

This example demonstrates how to create a simple chatbot that remembers conversation history using the SupabaseSaver.

import { SupabaseSaver } from '@skroyc/langgraph-supabase-checkpointer';
import { createClient } from '@supabase/supabase-js';
import { createReactAgent } from '@langchain/langgraph/prebuilt';
import { ChatGoogleGenerativeAI } from '@langchain/google-genai';
import { v4 as uuidv4 } from 'uuid';

// 1. Initialize Supabase client
const supabaseClient = createClient(
  process.env.SUPABASE_URL!,
  process.env.SUPABASE_ANON_KEY!,
  {
    auth: { persistSession: false } // Recommended for server-side usage
  }
);

// 2. Create a checkpoint saver instance (for a specific user)
const checkpointer = new SupabaseSaver(supabaseClient, undefined, 'user-123');

// 3. Create a LangGraph agent with persistence enabled
const agent = await createReactAgent({
  tools: [], // Your tools here
  llm: new ChatGoogleGenerativeAI({
    model: 'gemini-2.5-flash',
    apiKey: process.env.GOOGLE_API_KEY!,
    temperature: 0.7,
  }),
  checkpointSaver: checkpointer,
});

// 4. Use the agent with a persistent thread_id
async function chatWithMemory(threadId: string, message: string) {
  const result = await agent.invoke(
    { messages: [{ role: "user", content: message }] },
    {
      configurable: {
        thread_id: threadId,
        metadata: {
          app_version: '1.0.0',
          environment: process.env.NODE_ENV!,
        },
      }
    }
  );

  // The final message is usually the last one in the array
  const lastMessage = result.messages[result.messages.length - 1];
  return lastMessage.content;
}

// --- Run the example ---
async function runExample() {
  const threadId = uuidv4(); // V2: Generate UUID for thread ID

  // First message
  console.log('You: Hello, my name is Alice');
  const response1 = await chatWithMemory(threadId, 'Hello, my name is Alice');
  console.log('AI:', response1);

  // Second message - the AI should remember the context from the Supabase checkpointer
  console.log('\nYou: What is my name?');
  const response2 = await chatWithMemory(threadId, 'What is my name?');
  console.log('AI:', response2);
}

runExample().catch(console.error);

Expected Output

You: Hello, my name is Alice
AI: Hello Alice! How can I assist you today?

You: What is my name?
AI: Your name is Alice! How can I help you today, Alice?

📚 Documentation

🔍 API Reference

SupabaseSaver Class

The main class that implements the BaseCheckpointSaver interface from LangGraph.

new SupabaseSaver(client, serde?, userId?)

Constructor Parameters:

  • client: SupabaseClient: The Supabase client instance from @supabase/supabase-js.
  • serde?: SerializerProtocol: (Optional) A custom serialization protocol. Defaults to LangGraph's JsonPlusSerializer.
  • userId?: string: (Optional) The user ID for server-side usage. If not provided, the checkpointer will attempt to retrieve it from the client's auth session.

Core Methods

All methods implement the BaseCheckpointSaver interface.

getTuple(config: RunnableConfig): Promise<CheckpointTuple | undefined>

Retrieves a checkpoint tuple for a given configuration.

const checkpoint = await checkpointer.getTuple({
  configurable: {
    thread_id: "my-thread",
    // Optional: Specify a specific checkpoint ID
    checkpoint_id: "specific-checkpoint-id"
  }
});
put(config: RunnableConfig, checkpoint: Checkpoint, metadata?: Record<string, unknown>): Promise<RunnableConfig>

Stores a checkpoint with its metadata atomically.

putWrites(config: RunnableConfig, writes: PendingWrite[], taskId: string): Promise<void>

Stores pending writes for a checkpoint.

list(config: RunnableConfig, options?: ListOptions): AsyncGenerator<CheckpointTuple>

Lists checkpoints matching a configuration, with support for filtering and pagination.

deleteThread(threadId: string): Promise<void>

Permanently deletes a thread and all its associated data, including checkpoints and stored blobs.

🔒 Authentication Modes

Server-side (Recommended)

When using the checkpointer in a secure backend environment like a Supabase Edge Function or a Node.js server, pass the userId directly to the constructor. This is the most secure and reliable method.

// Pass the user's ID directly for data isolation
const checkpointer = new SupabaseSaver(supabaseClient, undefined, user.id);

Client-side

If used on the client-side (e.g., in a browser), the checkpointer will rely on the active Supabase auth session to get the user ID. Ensure the user is authenticated.

// Will automatically get user ID from the active auth session
const checkpointer = new SupabaseSaver(supabaseClient);

⚙️ Advanced Configuration

You can control thread management through the configurable object.

const config = {
  configurable: {
    // Required: The ID for the conversation thread (V2: must be UUID)
    thread_id: uuidv4(), // or existing UUID string

    // Optional: A namespace to further segment checkpoints within a thread
    checkpoint_ns: "optional-namespace", // Default: ""

    // Optional: A specific checkpoint ID to get or resume from
    checkpoint_id: "specific-checkpoint-id"
  }
};

💡 Examples

Basic Chatbot with Memory

This snippet shows how two separate invocations on the same thread_id maintain context.

import { SupabaseSaver } from '@skroyc/langgraph-supabase-checkpointer';
import { createReactAgent } from '@langchain/langgraph/prebuilt';
import { HumanMessage } from '@langchain/core/messages';
import { v4 as uuidv4 } from 'uuid';

const checkpointer = new SupabaseSaver(supabaseClient, undefined, 'user-123');
const agent = createReactAgent({ tools: [], llm, checkpointSaver: checkpointer });

// V2: Use UUID for thread ID
const threadId = uuidv4();

// First invocation
await agent.invoke(
  { messages: [new HumanMessage("My name is Alice")] },
  { configurable: { thread_id: threadId } }
);

// Later invocation - the agent will remember the name "Alice"
const response = await agent.invoke(
  { messages: [new HumanMessage("What's my name?")] },
  { configurable: { thread_id: threadId } }
);

Supabase Edge Function Integration

Here is how you can securely use the checkpointer within a Supabase Edge Function.

// In your Supabase Edge Function (e.g., supabase/functions/chat/index.ts)
import { createClient } from '@supabase/supabase-js';
import { SupabaseSaver } from '@skroyc/langgraph-supabase-checkpointer';

const supabaseClient = createClient(Deno.env.get('SUPABASE_URL')!, Deno.env.get('SUPABASE_ANON_KEY')!);

Deno.serve(async (req) => {
  // Securely get the user from the Authorization header
  const { data: { user } } = await supabaseClient.auth.getUser(
    req.headers.get('Authorization')?.replace('Bearer ', '') ?? ''
  );

  if (!user) {
    return new Response('Unauthorized', { status: 401 });
  }

  // Instantiate the checkpointer with the authenticated user's ID
  const checkpointer = new SupabaseSaver(supabaseClient, undefined, user.id);

  // ... now use the checkpointer with your LangGraph agent
  // const agent = createReactAgent({ ..., checkpointSaver: checkpointer });
  // const result = await agent.invoke(...);

  return new Response(JSON.stringify({ message: "Success" }), {
    headers: { 'Content-Type': 'application/json' },
  });
});

🏗️ Architecture

Database Schema

The schema is designed for performance and scalability by separating checkpoint metadata from large binary data, which is offloaded to Supabase Storage.

  • checkpoints: Stores the core checkpoint data, metadata, and parent references.
  • checkpoint_blobs: Stores TEXT paths that point to the actual blob data in Supabase Storage. This keeps the database table small and lookups fast.
  • checkpoint_writes: Stores pending writes as bytea before they are consolidated into a checkpoint.

Key Design Decisions

  1. Supabase Storage for Blobs: Large binary data (channel values) is uploaded to Supabase Storage. The database only stores the path to the object, which is more efficient for database performance and allows for leveraging Supabase's dedicated file storage infrastructure.
  2. Atomic RPC Functions: Using PostgreSQL functions (put_checkpoint_and_blobs) ensures that checkpoints and their associated blob metadata are written in a single, atomic transaction, preventing data inconsistency.
  3. User-based Isolation: Row Level Security (RLS) policies are enabled by default to ensure users can only access their own data, both in the database and in the storage bucket.
  4. Efficient Serialization: Uses LangGraph's default JsonPlusSerializer for broad compatibility.

🔍 Performance Considerations

The implementation includes several database optimizations:

  • Efficient Indexing: Indexes are created on thread_id, user_id, and created_at to accelerate common query patterns like fetching the latest checkpoint or listing a thread's history.
  • Atomic Operations: PostgreSQL functions reduce network round-trips and ensure data consistency.
  • Row Level Security: RLS is highly performant in PostgreSQL and provides robust, built-in security without application-level overhead.

🛠️ Troubleshooting

"Function not found" errors

This almost always means the database migration script was not run or did not complete successfully. Please re-run the SQL script from the Database Setup section.

"User not authenticated" or RLS violation errors

Ensure the user is properly authenticated when using client-side mode. For server-side usage, verify that you are correctly passing a valid userId to the SupabaseSaver constructor.

Debugging with SQL

You can inspect the database tables directly to debug issues:

-- View the 10 most recent checkpoints
SELECT thread_id, checkpoint_id, metadata, created_at FROM public.checkpoints ORDER BY created_at DESC LIMIT 10;

-- View recent blob storage paths
SELECT thread_id, channel, value as storage_path, type FROM public.checkpoint_blobs ORDER BY created_at DESC LIMIT 10;

-- View pending writes
SELECT thread_id, checkpoint_id, task_id, channel FROM public.checkpoint_writes ORDER BY created_at DESC LIMIT 10;

🙏 Acknowledgements

  • LangChain for the amazing LangGraph framework.
  • Supabase for their incredible developer platform.

📄 License

This project is licensed under the MIT License - see the LICENSE file for details.