Nodes and edges are the whole vocabulary of a LangGraph workflow.
A node is a step of work: any Python function that takes the state and returns an update. You add it with a name:
builder.add_node("classify", classify)If you pass only the function, its name is used as the node name.
An edge says what runs next. builder.add_edge("classify", "answer") means: after classify finishes, run answer.
Two special names mark the boundaries:
START: where the run begins.add_edge(START, "classify")makesclassifythe first node.END: where the run stops.add_edge("answer", END)finishes afteranswer.
Both are imported from langgraph.graph.
Nodes can do anything: call a model, query a database, run plain Python logic. That is a strength of LangGraph: deterministic steps (fixed code that always behaves the same) and model-driven steps live side by side.
If one node has edges to two different nodes, both run in parallel in the next step, and a node with edges from both waits for them. That is how you fan out work and join the results.
Keep nodes focused: one clear job each, like "retrieve documents" or "draft reply". Small nodes are easier to test, retry, stream and debug, because LangGraph saves state between them.
Example
from typing import TypedDict
from langgraph.graph import StateGraph, START, END
class State(TypedDict):
text: str
words: int
shout: str
def count(state: State) -> dict:
return {"words": len(state["text"].split())}
def upper(state: State) -> dict:
return {"shout": state["text"].upper()}
builder = StateGraph(State)
builder.add_node(count) # node name = "count"
builder.add_node("upper", upper)
builder.add_edge(START, "count")
builder.add_edge("count", "upper")
builder.add_edge("upper", END)Remember
- add_node(name, fn) registers a step; the name defaults to the function name
- add_edge(a, b) runs b after a
- START and END mark where runs begin and finish
- Two edges out of one node run the targets in parallel