[GH-ISSUE #2154] [Documentation] How to access lifespan-initialized resources (app.state) in graph nodes when using langgraph dev? #272

Open
opened 2026-02-17 17:19:32 -05:00 by yindo · 2 comments
Owner

Originally created by @errajibadr on GitHub (Jan 9, 2026).
Original GitHub issue: https://github.com/langchain-ai/docs/issues/2154

Summary

The documentation for custom lifespan events explains how to initialize async resources (database connections, HTTP clients) at server startup and store them in app.state. However, it doesn't explain how to access these resources from within graph nodes.

Environment

langgraph==1.0.5
langgraph-api==0.6.15
langgraph-checkpoint==3.0.1
langgraph-checkpoint-sqlite==3.0.1
langgraph-cli==0.4.11
langgraph-prebuilt==1.0.5
langgraph-runtime-inmem==0.20.1
langgraph-sdk==0.3.1

  • Running via: langgraph dev

Current Setup

Following the lifespan documentation, I have a webapp.py:

from contextlib import asynccontextmanager
from fastapi import FastAPI
from my_package.adapters import AsyncElasticsearchAdapter

@asynccontextmanager
async def lifespan(app: FastAPI):
# Create async client in the correct event loop
es_adapter = AsyncElasticsearchAdapter(hosts=["http://localhost:9200"])
app.state.es_adapter = es_adapter
yield
await es_adapter.close()

app = FastAPI(lifespan=lifespan)And referenced in langgraph.json:

{
"http": {
"app": "my_package.webapp:app"
},
"graphs": {
"my_agent": "my_package.agent:graph"
}
}

Question

How do I access app.state.es_adapter from within a graph node?

I understand the Runtime context pattern for passing dependencies:

from langgraph.runtime import Runtime

def my_node(state: State, runtime: Runtime[MyContext]):
adapter = runtime.context.es_adapter # How to get this?
# ...But I don't see how to bridge the lifespan-initialized app.state to the context that gets passed to graph invocations.

Possible approaches I've considered:

  1. Global module variable - Works but feels anti-pattern for async resources
  2. Custom middleware that injects app.state into config/context - Not documented
  3. Custom route handler that wraps the standard invoke endpoint - Seems like reimplementing the wheel

Expected Behavior

Ideally, the documentation would show a complete example of:

  1. Initializing a database/HTTP client in lifespan
  2. Accessing that client in a graph node
  3. Properly cleaning up on shutdown

Related

Thank you for any guidance!

Originally created by @errajibadr on GitHub (Jan 9, 2026). Original GitHub issue: https://github.com/langchain-ai/docs/issues/2154 ## Summary The documentation for [custom lifespan events](https://docs.langchain.com/langsmith/custom-lifespan) explains how to initialize async resources (database connections, HTTP clients) at server startup and store them in `app.state`. However, it doesn't explain how to access these resources from within graph nodes. ## Environment langgraph==1.0.5 langgraph-api==0.6.15 langgraph-checkpoint==3.0.1 langgraph-checkpoint-sqlite==3.0.1 langgraph-cli==0.4.11 langgraph-prebuilt==1.0.5 langgraph-runtime-inmem==0.20.1 langgraph-sdk==0.3.1 - Running via: `langgraph dev` ## Current Setup Following the lifespan documentation, I have a `webapp.py`: from contextlib import asynccontextmanager from fastapi import FastAPI from my_package.adapters import AsyncElasticsearchAdapter @asynccontextmanager async def lifespan(app: FastAPI): # Create async client in the correct event loop es_adapter = AsyncElasticsearchAdapter(hosts=["http://localhost:9200"]) app.state.es_adapter = es_adapter yield await es_adapter.close() app = FastAPI(lifespan=lifespan)And referenced in `langgraph.json`: { "http": { "app": "my_package.webapp:app" }, "graphs": { "my_agent": "my_package.agent:graph" } } ## Question How do I access `app.state.es_adapter` from within a graph node? I understand the `Runtime` context pattern for passing dependencies: from langgraph.runtime import Runtime def my_node(state: State, runtime: Runtime[MyContext]): adapter = runtime.context.es_adapter # How to get this? # ...But I don't see how to bridge the lifespan-initialized `app.state` to the `context` that gets passed to graph invocations. ### Possible approaches I've considered: 1. **Global module variable** - Works but feels anti-pattern for async resources 2. **Custom middleware** that injects `app.state` into config/context - Not documented 3. **Custom route handler** that wraps the standard invoke endpoint - Seems like reimplementing the wheel ## Expected Behavior Ideally, the documentation would show a complete example of: 1. Initializing a database/HTTP client in lifespan 2. Accessing that client in a graph node 3. Properly cleaning up on shutdown ## Related - [Custom lifespan events docs](https://docs.langchain.com/langsmith/custom-lifespan) Thank you for any guidance!
yindo added the external label 2026-02-17 17:19:32 -05:00
Author
Owner

@errajibadr commented on GitHub (Jan 10, 2026):

OR at least I'm looking on how to trigger a hook like per_req_config_modifier for the served graphs to inject the dependency in the graph config/context

i saw. that in langgserve for example we can do

`

server.py

from contextlib import asynccontextmanager
from typing import Any, Dict

import httpx
from fastapi import FastAPI, Request
from langchain_core.runnables import RunnableConfig
from langgraph.graph import StateGraph

from langserve import add_routes

1. Lifespan creates your async client

@asynccontextmanager
async def lifespan(app: FastAPI):
app.state.http_client = httpx.AsyncClient(timeout=30.0)
yield
await app.state.http_client.aclose()

app = FastAPI(lifespan=lifespan)

2. This hook is called by the AUTO-GENERATED routes

async def per_req_config_modifier(config: Dict[str, Any], request: Request) -> Dict[str, Any]:
"""Called automatically by LangServe on /invoke, /stream, /batch, /stream_events."""
config = config.copy()
config["configurable"] = config.get("configurable", {})
config["configurable"]["http_client"] = request.app.state.http_client
return config

3. Your graph accesses the client from config

async def my_node(state: dict, config: RunnableConfig) -> dict:
http_client = config["configurable"]["http_client"]
# use http_client...
return {"result": "..."}

builder = StateGraph(dict)
builder.add_node("fetch", my_node)
builder.set_entry_point("fetch")
builder.set_finish_point("fetch")
graph = builder.compile()

add_routes(
app,
graph,
path="/mygraph",
per_req_config_modifier=per_req_config_modifier, # <-- just a hook, not a custom route
)
`

But i don't want to dod add_routes since i fully relly on langgraph on langgraph server wrapping autatically my agents

@errajibadr commented on GitHub (Jan 10, 2026): OR at least I'm looking on how to trigger a hook like per_req_config_modifier for the served graphs to inject the dependency in the graph config/context i saw. that in langgserve for example we can do ` # server.py from contextlib import asynccontextmanager from typing import Any, Dict import httpx from fastapi import FastAPI, Request from langchain_core.runnables import RunnableConfig from langgraph.graph import StateGraph from langserve import add_routes # 1. Lifespan creates your async client @asynccontextmanager async def lifespan(app: FastAPI): app.state.http_client = httpx.AsyncClient(timeout=30.0) yield await app.state.http_client.aclose() app = FastAPI(lifespan=lifespan) # 2. This hook is called by the AUTO-GENERATED routes async def per_req_config_modifier(config: Dict[str, Any], request: Request) -> Dict[str, Any]: """Called automatically by LangServe on /invoke, /stream, /batch, /stream_events.""" config = config.copy() config["configurable"] = config.get("configurable", {}) config["configurable"]["http_client"] = request.app.state.http_client return config # 3. Your graph accesses the client from config async def my_node(state: dict, config: RunnableConfig) -> dict: http_client = config["configurable"]["http_client"] # use http_client... return {"result": "..."} builder = StateGraph(dict) builder.add_node("fetch", my_node) builder.set_entry_point("fetch") builder.set_finish_point("fetch") graph = builder.compile() add_routes( app, graph, path="/mygraph", per_req_config_modifier=per_req_config_modifier, # <-- just a hook, not a custom route ) ` But i don't want to dod add_routes since i fully relly on langgraph on langgraph server wrapping autatically my agents
Author
Owner

@errajibadr commented on GitHub (Feb 1, 2026):

Hello, Any updates, comments?

@errajibadr commented on GitHub (Feb 1, 2026): Hello, Any updates, comments?
yindo changed title from [Documentation] How to access lifespan-initialized resources (app.state) in graph nodes when using langgraph dev? to [GH-ISSUE #2154] [Documentation] How to access lifespan-initialized resources (app.state) in graph nodes when using langgraph dev? 2026-06-05 17:26:00 -04:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: langchain-ai/docs#272