The installation step looks simple, but it shapes every later lesson. A rushed setup usually produces the worst kind of beginner frustration: import errors, mismatched package versions, and examples that fail before the graph logic even starts.
A clean LangGraph environment has three jobs. It must isolate dependencies, keep provider packages explicit, and make it obvious where graph code, tool code, prompts, and configuration belong as the project grows.
The goal of this page is not just to get `pip install` working. It is to give you a setup you can still live with when the tutorial becomes a real codebase.
Use a dedicated virtual environment for every LangGraph project. Graph applications often combine model integrations, database adapters, checkpoint packages, tracing clients, and testing libraries. Sharing those across unrelated projects creates version drift quickly.
Keep your Python version modern and stable. When a tutorial mixes async execution, typing, and provider SDKs, recent Python versions reduce friction and make example code cleaner.
The core package gives you `StateGraph`, routing primitives, and graph execution. Add provider-specific model packages only after the base graph import works. This isolates setup failures to one layer at a time.
Official docs now separate graph runtime concerns from provider integrations. That is healthy for production too: your graph architecture should not be tightly coupled to one model vendor.
Even in a tutorial repo, separate graph definitions from node implementations. Nodes are easier to unit test when they are plain functions in their own module. Tools, database clients, and prompts should also live outside the graph wiring file.
The graph file should read like an architecture diagram: state definitions, node registration, edge definitions, compilation. When business logic and graph wiring mix heavily, the graph stops being readable.
Do a thin smoke test before you start writing real workflows. Import the graph package, compile a one-node graph, and invoke it with a tiny state object. That test catches the majority of early environment problems.
If you plan to use persistence, verify the checkpointer in isolation too. Storage-related issues often appear only after the graph compiles successfully, so treat checkpoint setup as a separate acceptance check.
Avoid burying secrets in node files or notebooks. Use environment variables or a settings layer from day one. LangGraph systems often evolve into services, and the cost of unwinding hard-coded credentials later is not worth the convenience.
The same goes for logging. Introduce structured logs and a shared configuration module early, even in small tutorial projects. Agent workflows are much easier to grow when the plumbing starts clean.
A good LangGraph setup should make experiments reproducible. Use a dedicated virtual environment, pin important package versions for projects, and keep model credentials, tracing configuration, and checkpoint settings outside source code. This prevents confusing bugs where a graph works on one machine because of hidden local state.
Start with the smallest possible graph after installation: one state schema, one node, one edge from START, and one edge to END. This proves that the environment, imports, and invocation path work before you add models, tools, persistence, or streaming. If the first graph is complicated, setup errors become harder to isolate.
Add project structure early. Keep graph construction, node functions, state definitions, tool wrappers, tests, and configuration in separate modules once the example grows. LangGraph applications become easier to maintain when the graph shape is not tangled with every backend implementation detail.
For serious work, configure tracing and checkpointing before the graph becomes complex. You do not need production infrastructure on day one, but you do need the habit of seeing state transitions, node outputs, and errors. Debugging without traces is possible for toy graphs and painful for real ones.
Create a clean starter project that includes a virtual environment, pinned dependencies, a minimal graph, one test, and a simple tracing configuration. This becomes a reusable baseline for future LangGraph experiments.
Then intentionally break one piece: remove a dependency, change a state field, or misconfigure an environment variable. A good setup should make the failure obvious. Learning how failure looks early makes larger projects much less intimidating.
A clean setup also helps collaboration: another developer should be able to install dependencies, run the smallest graph, and execute tests without asking for hidden steps.
For deeper learning, pair the concept with a tiny graph and a saved trace. Seeing state before and after each node makes the lesson concrete and gives learners a reliable debugging habit from the beginning.
This setup sequence gives you a minimal but reliable local environment.
# Windows PowerShell
py -3.11 -m venv .venv
.venv\Scripts\Activate.ps1
python -m pip install --upgrade pip
pip install langgraph
python - <<'PYCODE'
from langgraph.graph import StateGraph, START, END
from typing_extensions import TypedDict
class State(TypedDict):
text: str
def echo(state: State) -> dict:
return {"text": state["text"]}
builder = StateGraph(State)
builder.add_node("echo", echo)
builder.add_edge(START, "echo")
builder.add_edge("echo", END)
graph = builder.compile()
print(graph.invoke({"text": "setup works"}))
PYCODE
Official docs show `InMemorySaver` for development and testing. It lets you verify persistence-related wiring without external infrastructure.
from typing_extensions import TypedDict
from langgraph.graph import StateGraph, START, END
from langgraph.checkpoint.memory import InMemorySaver
class State(TypedDict):
count: int
def bump(state: State) -> dict:
return {"count": state["count"] + 1}
builder = StateGraph(State)
builder.add_node("bump", bump)
builder.add_edge(START, "bump")
builder.add_edge("bump", END)
checkpointer = InMemorySaver()
graph = builder.compile(checkpointer=checkpointer)
config = {"configurable": {"thread_id": "demo-thread"}}
print(graph.invoke({"count": 0}, config=config))
This file layout keeps graph assembly, nodes, tools, and tests separate so the tutorial code can grow into a service.
langgraph_app/
|-- pyproject.toml
|-- .env.example
|-- app/
| |-- settings.py
| |-- state.py
| |-- graphs/
| | `-- support_graph.py
| |-- nodes/
| | |-- classify.py
| | |-- retrieve.py
| | `-- draft.py
| `-- tools/
| `-- order_lookup.py
`-- tests/
|-- test_nodes.py
|-- test_routes.py
`-- test_graph_runs.py
Use whichever helps you learn fastest, but move into a normal project structure as soon as the workflow has more than one or two nodes.
No. Use in-memory persistence for local learning, then introduce a durable backend when you need pause-resume or shared services.
A tiny graph compiles and returns state successfully. Everything else builds on that proof.
Explore 500+ free tutorials across 20+ languages and frameworks.