Get AI summaries of any video or article — Sign up free
LangGraph + SQLite | Chatbot with Database Integration | CampusX thumbnail

LangGraph + SQLite | Chatbot with Database Integration | CampusX

CampusX·
5 min read

Based on CampusX's video on YouTube. If you like this content, support the original creators by watching, liking and subscribing to their content.

TL;DR

Replace the RAM-based LangGraph memory saver with a SQLite-backed checkpointer to persist chat state across app restarts and page reloads.

Briefing

The core upgrade is replacing a RAM-based “memory saver” with a SQLite-backed checkpointer so a LangGraph chatbot can keep conversations permanently. That change fixes a major reliability gap: previously, closing the app or reloading the page wiped chat history, forcing users to restart from scratch. With SQLite in place, messages and conversation state persist across restarts, letting users resume days later from the exact point they left off.

The chatbot’s earlier improvements—GUI support via Streamlit, token streaming for better user experience, and “threads” so multiple conversation histories can be resumed—remain intact. The new work targets what happens after the user refreshes or exits. Instead of storing all messages in memory, the system writes them into a database through LangGraph’s checkpointing mechanism. The result is a chatbot that can retrieve prior messages reliably and separate histories by thread ID.

Implementation starts with backend changes in the LangGraph code. A new file is created for the database-enabled backend (named in the transcript as “LangGraph Database Backend.py”). The key technical step is installing an external library called “langgraph-checkpoint-sqlite,” then swapping the checkpointer from “langgraph.checkpoint.memory” to a SQLite-based saver. Because SQLite connections are thread-sensitive, the setup includes a “check_same_thread” parameter set to False to avoid runtime errors when the workflow later uses multiple threads for concurrent conversations.

Next comes the database connection wiring: the code imports sqlite3, creates (or reuses) a local database file (shown as “lets_chatbot.db”), and passes the resulting connection into the SQLite checkpointer. After that, the rest of the chatbot invocation logic stays essentially the same—messages still go through the LangGraph chatbot, but now the checkpointing layer persists outputs.

A test confirms the behavior. Running a simple chat sequence creates the database file automatically in the project directory (“chatbot.db” in the transcript’s naming). Re-running the same prompt after the program exits shows the chatbot can answer using previously stored context, demonstrating that the conversation state survived the restart. The transcript also shows thread isolation: switching from “thread one” to “thread two” stores separate histories, and asking “what is my name” returns the correct name for each thread.

To make the persistence tangible, the database is visualized using a VS Code extension (a “SQLite Viewer” extension). The checkpointer creates multiple checkpoints per thread—checkpoint objects appear repeatedly because the workflow generates checkpoints at different stages (start, near the chat node, and end). The transcript demonstrates drilling into a specific checkpoint to view the stored messages and metadata.

Finally, the Streamlit frontend is updated so it no longer initializes with empty thread lists. Instead, it queries the backend for existing thread IDs from the SQLite checkpoint store, then populates the session state accordingly. When the UI reloads, the thread list and prior conversations reappear. The practical outcome: users can close the app, reopen it later, and still see all prior threads and messages—whether they were “Nitesh,” “Rahul,” or any other conversation thread created earlier.

Cornell Notes

The chatbot’s persistence problem is solved by moving from a RAM-based memory saver to a SQLite-backed checkpointer in LangGraph. Messages and conversation state are written into a local SQLite database (e.g., “lets_chatbot.db”), so closing the app or refreshing the page no longer erases chat history. The system keeps separate histories using LangGraph “threads,” and the frontend is updated to load existing thread IDs from the database at startup. A test run shows the database file is created automatically, and re-running prompts retrieves prior context. Visualizing the SQLite database confirms multiple checkpoints per thread are stored, enabling reliable resumption of conversations across restarts.

Why does chat history disappear in the earlier setup, and what exactly changes to prevent that?

Earlier, the chatbot used an in-memory “memory saver,” which stores conversation state in RAM. When the app closes or the page reloads, RAM is cleared, so all prior messages vanish. The fix replaces that with a SQLite-based checkpointer (“langgraph-checkpoint-sqlite”), which persists checkpoints to a disk-backed database file. After this swap, the chatbot can reload prior thread state because the checkpoint data remains in SQLite.

How does SQLite checkpointing work with LangGraph threads?

Thread IDs act as the key for separating conversation histories. When a user chats under “thread one,” the checkpoint store records messages under that thread’s context; switching to “thread two” records a different set of messages. The transcript demonstrates that asking “what is my name” returns “Nitesh” for thread one and “Rahul” for thread two, proving thread-scoped persistence.

What is the role of the “check_same_thread” setting in the SQLite connection?

SQLite connections are restricted to the thread that created them. If the code later uses multiple threads, attempting to reuse the same SQLite connection from another thread can trigger errors. Setting “check_same_thread” to False relaxes that restriction so the workflow can operate without crashing when multiple conversation threads are handled.

How is persistence verified beyond just trusting the code?

The transcript runs a chat, confirms the response, then checks that a database file (e.g., “chatbot.db”) appears in the project directory. After restarting/rerunning, the same prompt produces answers that rely on previously stored messages. It also verifies thread separation by changing thread IDs and observing that the chatbot responds with the correct prior context for each thread.

Why are there multiple checkpoints per thread in the SQLite database?

The workflow generates checkpoints at several stages—at the start, near the chat node, and at the end. Because the same thread is executed multiple times (e.g., multiple user prompts), the checkpoint store accumulates multiple checkpoint entries for that thread. That’s why the database shows repeated checkpoint records even when the number of unique threads is small.

What frontend change is required so the UI doesn’t start with empty threads after a reload?

The Streamlit session initialization previously started with an empty thread list because persistence didn’t exist. Now the frontend must query the backend for existing thread IDs from the SQLite checkpoint store. The transcript implements a function (e.g., “retrieve all threads”) that extracts unique thread IDs from the checkpoint list, then populates the session state so old threads and messages reappear immediately on load.

Review Questions

  1. What failure mode does switching from an in-memory memory saver to a SQLite checkpointer address, and how does it show up to users?
  2. How does the system ensure that thread one and thread two do not mix their conversation histories?
  3. What does the frontend need to do at startup to display existing threads after a refresh, and where does that information come from?

Key Points

  1. 1

    Replace the RAM-based LangGraph memory saver with a SQLite-backed checkpointer to persist chat state across app restarts and page reloads.

  2. 2

    Install and use the external library “langgraph-checkpoint-sqlite” to enable SQLite checkpointing in LangGraph.

  3. 3

    Create and connect a sqlite3 database file (e.g., “lets_chatbot.db”) and pass the connection into the SQLite checkpointer.

  4. 4

    Set “check_same_thread” to False to avoid SQLite thread-affinity errors when multiple conversation threads are used.

  5. 5

    Test persistence by running a chat, confirming the database file is created, then re-running after restart to verify prior context is retrieved.

  6. 6

    Update the Streamlit frontend to load existing thread IDs from the SQLite checkpoint store instead of initializing with an empty thread list.

  7. 7

    Use a SQLite viewer (e.g., a VS Code extension) to inspect checkpoints and confirm thread-scoped message storage.

Highlights

The persistence fix is a single architectural swap: RAM checkpoints become SQLite checkpoints, so conversations survive closing the app.
Thread IDs drive separation—switching from “thread one” to “thread two” yields different stored histories and different answers.
SQLite checkpointing creates multiple checkpoint entries per thread because checkpoints are generated at multiple workflow stages (start, node, end).
The frontend must query the backend for existing thread IDs at startup; otherwise the UI would still look empty after reloads.

Topics

  • LangGraph Checkpointing
  • SQLite Persistence
  • Streamlit Threads
  • Chatbot Memory
  • Database Integration

Mentioned