Skip to content

langgraph4j-postgres-saver

Persist and manage your langgraph4j workflow state in a PostgreSQL database for its durability**

Overview

langgraph4j-postgres-saver is a module for the langgraph4j ecosystem that enables persistent, reliable storage of workflow state in a PostgreSQL database. This makes your LLM-based applications stateful across executions—ensuring that workflow progress is not lost and can be resumed or analyzed at any point.

Key features include: - PostgreSQL-backed persistence: All workflow states are stored in a PostgreSQL database, surviving process restarts or system failures. - State caching: In-memory cache for state data to optimize performance by minimizing database round-trips during workflow execution. - Schema provisioning: Built-in services to easily create the required database schema for storing workflow states.

Features

  • Durable State: Persist the entire state of your langgraph4j workflow, allowing continuation or recovery at any time.
  • Performance Caching: Automatic in-memory caching reduces load on the database and accelerates repeated workflow invocations.
  • Easy Schema Initialization: Helper services are provided to create the required tables and structures in your PostgreSQL instance.
  • Seamless Integration: Works out of the box with langgraph4j’s state management and workflow APIs.

Requirements

  • PostgreSQL Database: Version 16.4 or higher recommended.
  • Java 17+
  • langgraph4j core library

Getting Started

Add Dependency

Add the following to your project's build configuration:

Maven

<dependency>
    <groupId>langgraph4j</groupId>
    <artifactId>langgraph4j-postgres-saver</artifactId>
    <version>1.6.0-beta4</version>
</dependency>

Gradle

implementation 'langgraph4j:langgraph4j-postgres-saver:1.6.0-beta4'

Initialize the PostgresSaver

The PostgresSaver is configured using a builder pattern. You need to provide your database connection parameters and a state serializer.

var saver = PostgresSaver saver = PostgresSaver.builder()
    .host("localhost")
    .port(5432)
    .user("the user name")
    .password("your password")
    .database("database name")
    .stateSerializer( stateSerializer )
    .dropTablesFirst( true | false ) // true try to drop table first. default is false
    .createTables( true | false ) // create tables if don't exist. default is false except if dropTablesFirst = true

Example Usage

Below is a complete example of how to use langgraph4j-postgres-saver to persist, reload, and verify workflow state:

public void testCheckpointWithNotReleasedThread() throws Exception {

    var stateSerializer = new ObjectStreamStateSerializer<>( AgentState::new );

    // Init Checkpoints saver
    var saver = PostgresSaver.builder()
            .host("localhost")
            .port(5432)
            .user("admin")
            .password("bsorrentino")
            .database("lg4j-store")
            .stateSerializer( stateSerializer )
            .createTables( true )
            .build();

    NodeAction<AgentState> agent_1 = state -> {
        log.info( "agent_1");
        return Map.of("agent_1:prop1", "agent_1:test");
    };

    var graph = new StateGraph<>(AgentState::new)
            .addNode("agent_1", node_async( agent_1 ))
            .addEdge( START,"agent_1")
            .addEdge( "agent_1",  END)
            ;

    var compileConfig = CompileConfig.builder()
            .checkpointSaver(saver)
            .releaseThread(false)
            .build();

    var runnableConfig = RunnableConfig.builder().build();
    var workflow = graph.compile( compileConfig );

    Map<String, Object> inputs = Map.of( "input", "test1");

    var result = workflow.invoke( inputs, runnableConfig );

    // Get checkpoint history
    var history = workflow.getStateHistory( runnableConfig ); 

    assertFalse( history.isEmpty() );
    assertEquals( 2, history.size() );

    // Get last saved checkpoint
    var lastSnapshot = workflow.lastStateOf( runnableConfig );

    assertTrue( lastSnapshot.isPresent() );
    assertEquals( "agent_1", lastSnapshot.get().node() );
    assertEquals( END, lastSnapshot.get().next() );

    // Test checkpoints reloading from database

    // Create a new saver (reset cache)
    saver =  PostgresSaver.builder()
            .host("localhost")
            .port(5432)
            .user("admin")
            .password("bsorrentino")
            .database("lg4j-store")
            .stateSerializer( stateSerializer )
            .build();

    compileConfig = CompileConfig.builder()
            .checkpointSaver(saver)
            .releaseThread(false)
            .build();

    runnableConfig = RunnableConfig.builder().build();
    workflow = graph.compile( compileConfig );

    history = workflow.getStateHistory( runnableConfig );

    assertFalse( history.isEmpty() );
    assertEquals( 2, history.size() );

    lastSnapshot = workflow.lastStateOf( runnableConfig );

    assertTrue( lastSnapshot.isPresent() );
    assertEquals( "agent_1", lastSnapshot.get().node() );
    assertEquals( END, lastSnapshot.get().next() );

    saver.release( runnableConfig );
}