A lean, open-source, production-shaped Python backend for LangGraph agents with FastAPI, PostgreSQL + pgvector, API-key auth, persistent conversations, Docker, migrations, and tests.
Quickstart · What you get · Architecture · Build your agent · API
Built for AI engineers turning a graph prototype into a durable REST API. Keep the backend plumbing, replace four small graph files, and focus on the behavior that makes your agent unique.
Use it as the starting point for a RAG assistant, internal copilot, support agent, research workflow, or AI SaaS backend.
Most agent tutorials stop at graph.invoke(). Real products still need authentication, tenant isolation, conversation history, migrations, health checks, structured errors, logs, containers, and a testable service boundary.
This repository provides that missing backend layer without turning it into a framework:
- Small enough to understand: the agent lives in four focused files.
- Serious enough to extend: auth, persistence, migrations, logging, CI, and tests are already wired.
- Deliberately boring infrastructure: one PostgreSQL database, explicit SQL, and no hidden runtime magic.
- Honest about scope: no UI, billing, background queue, or observability vendor is forced on you.
git clone https://github.com/IgnazioDS/langgraph-fastapi-starter.git
cd langgraph-fastapi-starter
cp .env.example .envSet the two required values in .env:
OPENAI_API_KEY=sk-...
POSTGRES_PASSWORD=localdevmake install
make up
make migrate
make create-key NAME="local-dev" ROLE="admin"
make devSave the API key printed by make create-key, then call the agent:
curl -X POST http://localhost:8000/v1/agent/run \
-H "Authorization: Bearer <your-api-key>" \
-H "Content-Type: application/json" \
-d '{
"session_id": "demo-1",
"message": "What makes a reliable production AI agent?"
}'You now have an authenticated agent API with PostgreSQL-backed conversation history. Open http://localhost:8000/docs for the interactive OpenAPI UI.
For the shortest clone → customize → run → test path, follow Build Your First Agent.
| Capability | Included implementation |
|---|---|
| Agent orchestration | Explicit LangGraph tool-calling loop with replaceable nodes, state, tools, and edges |
| API | Typed FastAPI routes, Pydantic v2 models, OpenAPI docs, and consistent error envelopes |
| Conversation memory | Tenant-aware sessions and message history persisted in PostgreSQL |
| RAG foundation | pgvector enabled in the first migration plus a document-retrieval extension point |
| Authentication | Revocable Bearer API keys, admin/user roles, tenant isolation, and hashed storage |
| Operations | Liveness/readiness endpoints, request IDs, structured JSON logs, Docker, and Gunicorn |
| Database changes | Reproducible Alembic migrations and parameterized SQL |
| Quality gates | pytest, Ruff, MyPy strict mode, and GitHub Actions CI |
Redis, Celery, OAuth, JWT sessions, file storage, billing, feature flags, an admin UI, and a mandatory observability platform. Add the pieces your product proves it needs.
flowchart LR
Client["Client / product UI"] --> API["FastAPI + Pydantic"]
API --> Auth["API-key auth + tenant context"]
Auth --> Service["AgentService"]
Service --> History[("PostgreSQL<br/>sessions + messages")]
Service --> Graph["LangGraph runtime"]
Graph --> LLM["OpenAI chat model"]
LLM -->|tool call| Tools["LangChain tools"]
Tools --> LLM
Graph --> Retrieval["Retrieval extension point"]
Retrieval -. optional .-> Vector[("pgvector<br/>documents table")]
The request path stays explicit: FastAPI handles transport, middleware establishes identity, the service loads and saves conversation history, and LangGraph owns the agent loop. PostgreSQL remains the only required datastore.
You do not need to understand the entire repository before customizing it. Your agent's behavior is concentrated in four files:
| File | Change it to… |
|---|---|
app/graph/state.py |
define the state your workflow carries |
app/graph/nodes.py |
implement reasoning, retrieval, validation, or routing steps |
app/graph/tools.py |
expose your product APIs and data as tools |
app/graph/graph.py |
connect nodes, branches, tool loops, and finish conditions |
The included research assistant is intentionally small. Replace it with a support copilot, document analyst, operations agent, lead-qualification workflow, or any domain-specific graph.
Choose this starter when you want:
- a Python agent backend your team can read in one sitting;
- LangGraph orchestration behind a conventional REST API;
- persistent multi-turn conversations without adding a second datastore;
- secure-by-default API access and an obvious path to multi-tenancy;
- infrastructure you can replace incrementally instead of framework lock-in.
Choose a larger platform when you already need:
- a visual workflow builder or hosted agent control plane;
- built-in distributed jobs, rate limiting, tracing dashboards, and model fallbacks;
- native multi-provider routing or a production UI out of the box;
- turnkey Kubernetes/Terraform infrastructure.
langgraph-fastapi-starter/
├── app/
│ ├── main.py # App factory, lifespan, middleware, routers
│ ├── config.py # Typed environment configuration
│ ├── graph/ # ← YOUR AGENT LIVES HERE
│ │ ├── state.py # Agent state shape
│ │ ├── nodes.py # Graph node functions
│ │ ├── tools.py # Agent tools and retrieval seam
│ │ └── graph.py # Graph assembly and routing
│ ├── routers/ # Agent, API-key, and health endpoints
│ ├── services/ # Agent execution and key management
│ ├── db/ # Connection pool and parameterized queries
│ ├── middleware/ # Authentication and structured logging
│ └── models/ # Pydantic request/response contracts
├── migrations/ # Alembic schema history
├── scripts/ # Key management and health utilities
├── tests/ # Router, service, and graph tests
├── docs/build-your-first-agent.md
├── docker-compose.yml # PostgreSQL + pgvector
├── Dockerfile # Production image
├── Makefile # Supported developer workflows
└── pyproject.toml
POST /v1/agent/run
Authorization: Bearer <api-key>
Content-Type: application/json
{
"session_id": "user-123-session-1",
"message": "Summarize the latest context and recommend the next action."
}{
"session_id": "user-123-session-1",
"response": "Agent response text",
"run_id": "run_20260402T143022000000",
"usage": {
"input_tokens": 142,
"output_tokens": 87
}
}The endpoint currently returns a standard JSON response. Native SSE streaming is a roadmap item.
GET /v1/agent/sessions/{session_id}
Authorization: Bearer <api-key>POST /v1/keys
Authorization: Bearer <admin-key>
Content-Type: application/json
{
"name": "production-app",
"role": "user",
"tenant_id": "customer-123"
}The plaintext key is returned once. Store it securely; it cannot be recovered.
DELETE /v1/keys/{key_id}
Authorization: Bearer <admin-key>GET /health # Public liveness probe
GET /health/detailed # Authenticated database + graph readiness probeThe starter creates three application tables:
api_keys (
id, key_hash, lookup_hash, name, tenant_id, role,
created_at, last_used_at, revoked_at
)
agent_sessions (
id, session_id, tenant_id, created_at, last_active_at, message_count
)
agent_messages (
id, session_id, role, content, metadata, created_at
)The initial migration also enables the pgvector extension. Add the document schema that fits your product when you are ready:
alembic revision -m "add_documents_table"PostgreSQL stores application data, conversation history, API keys, and—when you add a document table—vectors. This keeps the local and early-production stack understandable. Add Redis or a dedicated vector database when measured constraints justify it.
The data model is small and the queries are visible. Parameterized SQL keeps behavior predictable and makes it easy to understand exactly what every request does.
Bearer keys work for service-to-service and early product use cases. The middleware is deliberately isolated so you can swap in JWT or OAuth without rewriting graph or service code.
LangGraph makes state, conditional branches, and tool cycles visible. This starter persists conversation history in its service layer; it does not claim to configure a LangGraph checkpointer for you.
A synchronous JSON contract is easier to integrate, test, and operate. Add SSE when the product experience requires it rather than maintaining two response paths from day one.
Alembic keeps schema changes explicit, reversible, and auditable. Application startup verifies dependencies but does not create tables behind your back.
JSON logs and request IDs work with common log platforms without forcing a vendor SDK into the core application.
# Required
OPENAI_API_KEY=sk-...
POSTGRES_PASSWORD=localdev
# Database defaults
POSTGRES_HOST=localhost
POSTGRES_PORT=5432
POSTGRES_DB=agentdb
POSTGRES_USER=agent
DATABASE_POOL_SIZE=10
# Models
LLM_MODEL=gpt-4o-mini
EMBEDDING_MODEL=text-embedding-3-small
# Optional integrations
TAVILY_API_KEY= # Enables the web_search tool
# Application
APP_ENV=development # development | production
LOG_LEVEL=INFO
LOG_FORMAT=text # text | jsonDecorate a function with @tool in app/graph/tools.py, then add it to TOOLS. The model receives the updated tool set the next time the graph is initialized.
Implement the node in app/graph/nodes.py, register it in app/graph/graph.py, then connect it with a direct or conditional edge.
alembic revision -m "add_your_table"
# Edit the generated migration
alembic upgrade headKeep parameterized queries in app/db/queries.py and non-trivial business logic in app/services/.
Create the route in app/routers/, define its request/response contract in app/models/, and move reusable logic into a service.
The included Dockerfile runs Gunicorn with Uvicorn workers. Before a real deployment:
- run
alembic upgrade headas a pre-deploy step; - set
APP_ENV=productionto disable interactive API docs; - put the service behind TLS and a trusted reverse proxy;
- choose worker count from actual memory and latency measurements;
- replace or extend the auth layer for your product's identity model;
- add rate limiting, tracing, backups, and secret management appropriate to your environment.
gunicorn app.main:app \
--workers 4 \
--worker-class uvicorn.workers.UvicornWorker \
--bind 0.0.0.0:8000- Native Server-Sent Events streaming
- Optional LangGraph PostgreSQL checkpointer
- Pluggable model-provider adapters
- First-party tracing and evaluation hooks
- Deployment recipes for common cloud platforms
Have a strong use case for one of these? Start a GitHub Discussion.
Bug fixes, focused features, documentation improvements, and deployment recipes are welcome. Read CONTRIBUTING.md before opening a pull request, and use Discussions for larger design proposals.
Released under the MIT License. Use it, fork it, modify it, and ship it.
If this starter saves you backend work:
- Use the template for your next agent.
- Star the repository so more builders can find it.
- Share what you built in Discussions.
Questions and architecture ideas belong in Discussions; reproducible bugs belong in Issues.
Stack: Python 3.11+ · FastAPI · LangGraph · LangChain · OpenAI · PostgreSQL · pgvector · Alembic · Pydantic v2 · Docker · pytest · Ruff · MyPy