Code Beginner easy · 4 min

Adding nodes to the graph

What you will learn
Nodes are individual functions in your graph that perform one task: add them with <code>.add_node(name, function)</code>.

Why this matters

Every LLM workflow needs discrete steps (fetch data, call LLM, validate output, save result). Nodes organize these steps so LangGraph can execute, retry, and log each one independently.

Skip if: If your entire workflow is a single atomic operation with no intermediate steps or decision points, you don't need a graph: just call the function directly. Graphs add complexity when there's nothing to orchestrate.

Explanation

What it is: A node is a named Python function that does one thing. You register it with the graph using .add_node(name, function). When the graph runs, it will call that function at the right time.

How it works: Each node receives the current State (a dictionary-like object), performs work, and returns an updated state. The node's name becomes the reference point for edges (transitions). LangGraph tracks which nodes executed, their inputs, and their outputs: this is critical for debugging and monitoring.

When to use it: Break your workflow into nodes whenever you want independent execution, different error handling per step, or the ability to retry just one stage. A typical agent might have nodes for router, fetch_data, call_llm, and format_output.

Analogy

Think of nodes like assembly line stations. Each station (node) does one job: station A welds, station B paints, station C packages. The product (state) flows through each station in sequence. You can stop at any station to inspect, fix, or retry work: you don't have to redo the entire assembly.

Code

python
from langgraph.graph import StateGraph, START, END
from typing_extensions import TypedDict

class State(TypedDict):
    message: str
    result: str

def node_uppercase(state: State) -> State:
    state["result"] = state["message"].upper()
    return state

def node_add_exclamation(state: State) -> State:
    state["result"] = state["result"] + "!"
    return state

graph = StateGraph(State)
graph.add_node("uppercase", node_uppercase)
graph.add_node("exclaim", node_add_exclamation)

graph.add_edge(START, "uppercase")
graph.add_edge("uppercase", "exclaim")
graph.add_edge("exclaim", END)

runnable = graph.compile()

result = runnable.invoke({"message": "hello world", "result": ""})
print(f"Final result: {result['result']}")
Output
Final result: HELLO WORLD!

What just happened?

We defined a <code>State</code> schema with two fields. We created two node functions: <code>node_uppercase</code> reads <code>state["message"]</code>, converts it to uppercase, stores it in <code>state["result"]</code>, and returns the modified state. <code>node_add_exclamation</code> reads what the first node produced and appends an exclamation mark. We registered both nodes with <code>.add_node()</code>, then connected them with edges: START → uppercase → exclaim → END. When we called <code>.invoke()</code>, LangGraph executed uppercase first (got "HELLO WORLD"), passed its output state to exclaim (got "HELLO WORLD!"), then returned the final state.

Common gotcha

Nodes receive state by value, not reference. If you modify a nested structure inside state, you must return the entire state for changes to persist. Many developers forget to return state and wonder why their changes vanished.

Error recovery

KeyError when accessing state
You tried to read a key that doesn't exist in State. Check your TypedDict definition: the key must be declared there. Example: if State only has 'message', you cannot access state['result'] unless it's in the schema.
TypeError: add_node() got unexpected keyword argument
You're using old LangGraph syntax. Use positional arguments: .add_node('name', function), not .add_node(name='name', ...).
AttributeError: 'function' object has no attribute '__name__'
You passed a lambda or partial function that LangGraph cannot name. Use a regular def function instead, or give it a proper name.

Experienced dev note

A node doesn't have to return a completely new state object: you can mutate the dict in place and return it. Python passes dicts by reference. However, if you're using immutable state (dataclass with frozen=True), you must create a copy and return it. Always check your State definition. Also, node execution is synchronous in langgraph 0.2.x. If a node blocks for I/O, the entire graph stalls: use langgraph's built-in async support or external queues for high-throughput systems.

Check your understanding

If you have three nodes (A, B, C) connected A → B → C, and node B raises an exception, does LangGraph automatically retry just B, or must you implement retry logic separately? Why?

Show answer hint

The key insight is understanding langgraph's error handling scope: does the framework provide built-in retry-per-node, or is that your responsibility? Understanding this determines whether you need a separate retry decorator or if the graph handles it. langgraph 0.2.x does not auto-retry individual nodes; that's a manual pattern you implement.

VERSION < 0.2.0: Used StateGraph but required importing START, END as string constants ('START', '__end__'). langgraph 0.2.0+ requires explicit imports: from langgraph.graph import START, END. Always use the constant imports, not strings.
NEXT

Once nodes are defined, you'll learn how to connect them with <code>.add_edge()</code> to control execution order and handle conditional routing.

Community Notes

No notes yetBe the first to share a version-specific fix or tip.