LangGraph Tutorial (TypeScript): connecting to PostgreSQL for intermediate developers

By Cyprian AaronsUpdated 2026-04-22
langgraphconnecting-to-postgresql-for-intermediate-developerstypescript

This tutorial shows you how to wire a LangGraph TypeScript app to PostgreSQL so your graph can persist state across runs. You need this when you want resumable workflows, durable checkpoints, or shared conversation state that survives process restarts.

What You'll Need

  • Node.js 18+
  • A PostgreSQL instance running locally or in Docker
  • A database URL like postgresql://postgres:postgres@localhost:5432/langgraph_demo
  • An OpenAI API key if you want to use an LLM node
  • Packages:
    • @langchain/langgraph
    • @langchain/openai
    • pg
    • dotenv
    • typescript
    • tsx

Step-by-Step

  1. Set up the project and install dependencies. This gives you the LangGraph runtime, a Postgres client, and a simple way to run TypeScript directly.
mkdir langgraph-postgres-demo
cd langgraph-postgres-demo
npm init -y
npm install @langchain/langgraph @langchain/openai pg dotenv
npm install -D typescript tsx @types/node @types/pg
  1. Create your environment file and start PostgreSQL. LangGraph will use the database connection string through the checkpointer, so keep it in .env and load it at runtime.
cat > .env << 'EOF'
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/langgraph_demo
OPENAI_API_KEY=your_openai_key_here
EOF

docker run --name langgraph-pg \
  -e POSTGRES_USER=postgres \
  -e POSTGRES_PASSWORD=postgres \
  -e POSTGRES_DB=langgraph_demo \
  -p 5432:5432 \
  -d postgres:16
  1. Create the graph with a Postgres-backed checkpointer. The important part is PostgresSaver.fromConnString(...), which stores checkpoints in PostgreSQL instead of memory.
import "dotenv/config";
import { ChatOpenAI } from "@langchain/openai";
import { StateGraph, Annotation, START, END } from "@langchain/langgraph";
import { PostgresSaver } from "@langchain/langgraph/checkpoint-postgres";

const State = Annotation.Root({
  messages: Annotation<any[]>({
    reducer: (left, right) => left.concat(right),
    default: () => [],
  }),
});

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

async function main() {
  const checkpointer = await PostgresSaver.fromConnString(process.env.DATABASE_URL!);
  1. Add a node and compile the graph with persistence enabled. The node reads the current messages from state, calls the model, and returns the next message into the same thread.
  const app = new StateGraph(State)
    .addNode("chat", async (state) => {
      const response = await model.invoke(state.messages);
      return { messages: [response] };
    })
    .addEdge(START, "chat")
    .addEdge("chat", END)
    .compile({ checkpointer });

  const threadId = "customer-123";

  const firstRun = await app.invoke(
    { messages: [{ role: "user", content: "Write one sentence about PostgreSQL checkpoints." }] },
    { configurable: { thread_id: threadId } }
  );

  console.log(firstRun.messages.at(-1)?.content);
  1. Run it twice with the same thread ID to confirm state is persisted. On the second call, LangGraph loads the checkpoint from PostgreSQL before continuing execution.
  const secondRun = await app.invoke(
    { messages: [{ role: "user", content: "Now explain why this matters for resumable workflows." }] },
    { configurable: { thread_id: threadId } }
  );

  console.log(secondRun.messages.map((m) => m.content).join("\n"));
}

main().catch(console.error);

Testing It

Run the script with npx tsx index.ts. If everything is wired correctly, you should see model output on both invocations using the same thread_id.

To verify persistence, inspect your database after the first run:

docker exec -it langgraph-pg psql -U postgres -d langgraph_demo

Then query the checkpoint tables created by LangGraph. You should see rows associated with your thread after execution.

If you stop and restart your Node process, then run the script again with the same thread_id, LangGraph should continue using stored state instead of starting from scratch.

Next Steps

  • Add branching logic with conditional edges so your graph can route based on state.
  • Store richer structured state, not just messages, for claims intake or underwriting workflows.
  • Replace the single-node graph with multi-step agent flows and test rollback/resume behavior against PostgreSQL.

Keep learning

By Cyprian Aarons, AI Consultant at Topiax.

Want the complete 8-step roadmap?

Grab the free AI Agent Starter Kit — architecture templates, compliance checklists, and a 7-email deep-dive course.

Get the Starter Kit

Related Guides