- β‘ FastAPI for the Python backend API.
- π§° SQLModel for the Python SQL database interactions (ORM).
- π Pydantic, used by FastAPI, for the data validation and settings management.
- πΎ PostgreSQL as the SQL database.
- π React for the frontend.
- π Using TypeScript, hooks, Vite, and other parts of a modern frontend stack.
- π¨ Chakra UI for the frontend components.
- π€ An automatically generated frontend client.
- π§ͺ Playwright for End-to-End testing.
- π¦ Dark mode support.
- π Docker Compose for development and production.
- π Secure password hashing by default.
- π JWT (JSON Web Token) authentication.
- π« Email based password recovery.
- β Tests with Pytest.
- π Traefik as a reverse proxy / load balancer.
- π’ Deployment instructions using Docker Compose, including how to set up a frontend Traefik proxy to handle automatic HTTPS certificates.
- π CI (continuous integration) and CD (continuous deployment) based on GitHub Actions.
- π€ [NEW] LangGraph powered AI agents with LLM orchestration.
- π§ State-based agent workflows with planner/executor pattern.
- π Langfuse integration for LLM observability and tracing.
- π Automated evaluation framework with LLM-as-judge pattern.
- π‘οΈ Rate limiting with Redis to protect agent endpoints.
- π Prometheus metrics and Grafana dashboards for monitoring.
This template includes production-ready AI agent capabilities powered by LangGraph and LangChain. Follow these steps to get started with LLM features.
- An OpenAI API key (or Anthropic/Azure OpenAI)
- Optional: Langfuse account for observability (free cloud or self-hosted)
Copy the .env.local.example file to .env.local and add your API keys:
# Copy the local environment template
cp .env.local.example .env.local
# Edit the file and add your keys
nano .env.localMinimum required configuration:
# Your OpenAI API key
OPENAI_API_KEY=sk-...
# Enable agent features in frontend (optional)
VITE_ENABLE_AGENT=trueRecommended for development:
# Use faster, cheaper models for development
LLM_MODEL_NAME=gpt-3.5-turbo
# Enable Langfuse tracing (optional but recommended)
LANGFUSE_ENABLED=true
LANGFUSE_SECRET_KEY=sk-lf-...
LANGFUSE_PUBLIC_KEY=pk-lf-...
LANGFUSE_SAMPLE_RATE=1.0
# Evaluation API key (can be same as OPENAI_API_KEY)
EVALUATION_API_KEY=sk-...For production:
# Use production-grade models
LLM_MODEL_NAME=gpt-4
# Sample traces to reduce overhead (10% sampling)
LANGFUSE_SAMPLE_RATE=0.1
# Enable rate limiting
RATE_LIMIT_ENABLED=true
RATE_LIMIT_PER_MINUTE=60See .env.example for all available configuration options.
Start all services including Redis, Langfuse, Prometheus, and Grafana:
# Start services with live reload
docker compose watch
# Or for production
docker compose up -dAvailable services:
- Backend API: http://localhost:8000
- Frontend: http://localhost:5173
- API Docs: http://localhost:8000/docs
- Langfuse UI: http://localhost:3001
- Grafana: http://localhost:3002
- Prometheus: http://localhost:9090
Via Frontend (if VITE_ENABLE_AGENT=true):
- Login to the dashboard: http://localhost:5173
- Navigate to the Agent Chat
- Enter a prompt: "What users are in the system?"
- View the agent's response and click the trace link to see execution details in Langfuse
Via API:
# Get authentication token
TOKEN=$(curl -X POST http://localhost:8000/api/v1/login/access-token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "username=admin@example.com&password=changethisnowplease" \
| jq -r .access_token)
# Run the agent
curl -X POST http://localhost:8000/api/v1/agent/run \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"message": "What users are in the system?"
}'
# Get run history
curl http://localhost:8000/api/v1/agent/runs \
-H "Authorization: Bearer $TOKEN"Langfuse (Observability):
- Open http://localhost:3001
- Browse traces to see:
- LLM calls and responses
- Tool usage (database queries, HTTP requests)
- Token counts and costs
- Latency breakdown
- Error details
Grafana (Metrics):
- Open http://localhost:3002 (login: admin/admin)
- Navigate to Dashboards β Agent Performance
- View metrics:
- Request rates and success rates
- Latency percentiles (p50, p95, p99)
- Token usage over time
- Active agent executions
Prometheus (Raw Metrics):
- View raw metrics: http://localhost:8000/metrics
- Query metrics: http://localhost:9090
Evaluate agent outputs using the built-in evaluation framework:
# Activate backend environment
cd backend
source .venv/bin/activate
# Run evaluations on last 24 hours of traces
uv run python -m app.evaluation.cli
# View the generated report
cat reports/evaluation_*.json | jq .The evaluation system uses LLM-as-judge to score agent responses on:
- Conciseness: Response brevity and clarity
- Hallucination: Factual accuracy
- Helpfulness: How well it addresses the user's need
- Relevancy: Relevance to the query
- Toxicity: Harmful or inappropriate content
Scores are uploaded to Langfuse and included in trace metadata.
curl -X POST http://localhost:8000/api/v1/agent/run \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"message": "How many items does the admin user have?"
}'The agent will:
- Use the
lookup_user_by_emailtool to find the admin user - Use the
lookup_user_itemstool to count their items - Return a natural language response
# First message - agent generates a thread_id
RESPONSE=$(curl -X POST http://localhost:8000/api/v1/agent/run \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"message": "Who is the admin user?"}')
# Extract thread_id for conversation continuity
THREAD_ID=$(echo $RESPONSE | jq -r .thread_id)
# Follow-up message in same conversation
curl -X POST http://localhost:8000/api/v1/agent/run \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"message\": \"What items do they have?\",
\"thread_id\": \"$THREAD_ID\"
}"The agent maintains conversation context using PostgreSQL-backed memory.
curl -X POST http://localhost:8000/api/v1/agent/run \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"message": "Fetch the latest GitHub issues from fastapi/fastapi"
}'The agent can use the http_get tool to fetch external data.
Problem: Agent returns errors or timeouts.
Solutions:
-
Check API key:
# Test OpenAI API key curl https://api.openai.com/v1/models \ -H "Authorization: Bearer $OPENAI_API_KEY"
-
Check backend logs:
docker compose logs backend | grep -i error -
Verify model name:
- Ensure
LLM_MODEL_NAMEis valid (e.g.,gpt-4,gpt-3.5-turbo) - Check available models in OpenAI dashboard
- Ensure
-
Check rate limits:
- Review OpenAI usage: https://platform.openai.com/usage
- Temporarily disable rate limiting:
RATE_LIMIT_ENABLED=false
Problem: No traces visible in Langfuse UI.
Solutions:
-
Verify configuration:
# Check environment variables docker compose exec backend printenv | grep LANGFUSE
-
Check sampling rate:
- Ensure
LANGFUSE_SAMPLE_RATE > 0(1.0 = 100%) - Set
LANGFUSE_SAMPLE_RATE=1.0for development
- Ensure
-
Verify API keys:
- Check keys are correct in
.env.local - Test Langfuse connection: http://localhost:3001
- Check keys are correct in
-
Check backend logs:
docker compose logs backend | grep langfuse
Problem: Rate limiting fails with Redis errors.
Solutions:
-
Check Redis is running:
docker compose ps redis
-
Test Redis connection:
docker compose exec redis redis-cli ping # Should return: PONG
-
Restart Redis:
docker compose restart redis
-
Temporarily disable rate limiting:
# In .env.local RATE_LIMIT_ENABLED=false
Problem: Agent tables not found.
Solutions:
-
Run migrations:
docker compose exec backend alembic upgrade head -
Check migration status:
docker compose exec backend alembic current -
Reset database (development only):
docker compose down -v docker compose up -d
Problem: Unexpectedly high OpenAI bills.
Solutions:
-
Use cheaper models for development:
LLM_MODEL_NAME=gpt-3.5-turbo
-
Monitor token usage:
- View in Langfuse: http://localhost:3001
- Check Grafana metrics: http://localhost:3002
- Query Prometheus:
agent_tokens_total
-
Enable sampling in production:
LANGFUSE_SAMPLE_RATE=0.1 # Only trace 10% -
Reduce max tokens:
LLM_MAX_TOKENS=1024 # Lower limit
Problem: Evaluation CLI fails or returns no results.
Solutions:
-
Check evaluation API key:
# Should be set (can be same as OPENAI_API_KEY) echo $EVALUATION_API_KEY
-
Verify traces exist:
- Check Langfuse UI for traces from last 24 hours
- The CLI only evaluates unscored traces
-
Check evaluation logs:
cd backend source .venv/bin/activate uv run python -m app.evaluation.cli 2>&1 | tee evaluation.log
-
Increase sleep time if rate limited:
EVALUATION_SLEEP_TIME=2 # Wait 2 seconds between evaluations
Problem: Slow agent responses or need to measure system performance.
Solutions:
-
Run Performance Tests:
# Quick performance test ./scripts/run_performance_tests.sh # Custom test duration and concurrency DURATION=60 CONCURRENCY=25 ./scripts/run_performance_tests.sh
See Performance Testing Guide for detailed instructions.
-
Use faster models:
LLM_MODEL_NAME=gpt-3.5-turbo # Faster than gpt-4 -
Optimize tool usage:
- Reduce number of tools available
- Simplify tool implementations
-
Check infrastructure:
- Ensure adequate CPU/memory for Docker containers
- Monitor with Grafana: http://localhost:3002
- Run resource monitoring:
python3 scripts/monitor_resources.py
-
Reduce trace sampling:
LANGFUSE_SAMPLE_RATE=0.1 # Reduce overhead -
Review performance documentation:
- See docs/PERFORMANCE.md for comprehensive performance optimization guide
- Architecture Documentation: docs/architecture/llm-agents.md
- Performance Guide: docs/PERFORMANCE.md
- Performance Testing: docs/PERFORMANCE_TESTING_GUIDE.md
- LangGraph Documentation: https://langchain-ai.github.io/langgraph/
- Langfuse Documentation: https://langfuse.com/docs
- Backend Development: backend/README.md
- Frontend Development: frontend/README.md
You can just fork or clone this repository and use it as is.
β¨ It just works. β¨
If you want to have a private repository, GitHub won't allow you to simply fork it as it doesn't allow changing the visibility of forks.
But you can do the following:
- Create a new GitHub repo, for example
my-full-stack. - Clone this repository manually, set the name with the name of the project you want to use, for example
my-full-stack:
git clone git@github.com:fastapi/full-stack-fastapi-template.git my-full-stack- Enter into the new directory:
cd my-full-stack- Set the new origin to your new repository, copy it from the GitHub interface, for example:
git remote set-url origin git@github.com:octocat/my-full-stack.git- Add this repo as another "remote" to allow you to get updates later:
git remote add upstream git@github.com:fastapi/full-stack-fastapi-template.git- Push the code to your new repository:
git push -u origin masterAfter cloning the repository, and after doing changes, you might want to get the latest changes from this original template.
- Make sure you added the original repository as a remote, you can check it with:
git remote -v
origin git@github.com:octocat/my-full-stack.git (fetch)
origin git@github.com:octocat/my-full-stack.git (push)
upstream git@github.com:fastapi/full-stack-fastapi-template.git (fetch)
upstream git@github.com:fastapi/full-stack-fastapi-template.git (push)- Pull the latest changes without merging:
git pull --no-commit upstream masterThis will download the latest changes from this template without committing them, that way you can check everything is right before committing.
-
If there are conflicts, solve them in your editor.
-
Once you are done, commit the changes:
git merge --continueYou can then update configs in the .env files to customize your configurations.
Before deploying it, make sure you change at least the values for:
SECRET_KEYFIRST_SUPERUSER_PASSWORDPOSTGRES_PASSWORD
You can (and should) pass these as environment variables from secrets.
Read the deployment.md docs for more details.
Some environment variables in the .env file have a default value of changethis.
You have to change them with a secret key, to generate secret keys you can run the following command:
python -c "import secrets; print(secrets.token_urlsafe(32))"Copy the content and use that as password / secret key. And run that again to generate another secure key.
This repository also supports generating a new project using Copier.
It will copy all the files, ask you configuration questions, and update the .env files with your answers.
You can install Copier with:
pip install copierOr better, if you have pipx, you can run it with:
pipx install copierNote: If you have pipx, installing copier is optional, you could run it directly.
Decide a name for your new project's directory, you will use it below. For example, my-awesome-project.
Go to the directory that will be the parent of your project, and run the command with your project's name:
copier copy https://github.com/fastapi/full-stack-fastapi-template my-awesome-project --trustIf you have pipx and you didn't install copier, you can run it directly:
pipx run copier copy https://github.com/fastapi/full-stack-fastapi-template my-awesome-project --trustNote the --trust option is necessary to be able to execute a post-creation script that updates your .env files.
Copier will ask you for some data, you might want to have at hand before generating the project.
But don't worry, you can just update any of that in the .env files afterwards.
The input variables, with their default values (some auto generated) are:
project_name: (default:"FastAPI Project") The name of the project, shown to API users (in .env).stack_name: (default:"fastapi-project") The name of the stack used for Docker Compose labels and project name (no spaces, no periods) (in .env).secret_key: (default:"changethis") The secret key for the project, used for security, stored in .env, you can generate one with the method above.first_superuser: (default:"admin@example.com") The email of the first superuser (in .env).first_superuser_password: (default:"changethis") The password of the first superuser (in .env).smtp_host: (default: "") The SMTP server host to send emails, you can set it later in .env.smtp_user: (default: "") The SMTP server user to send emails, you can set it later in .env.smtp_password: (default: "") The SMTP server password to send emails, you can set it later in .env.emails_from_email: (default:"info@example.com") The email account to send emails from, you can set it later in .env.postgres_password: (default:"changethis") The password for the PostgreSQL database, stored in .env, you can generate one with the method above.sentry_dsn: (default: "") The DSN for Sentry, if you are using it, you can set it later in .env.
Backend docs: backend/README.md.
Frontend docs: frontend/README.md.
Deployment docs: deployment.md.
General development docs: development.md.
This includes using Docker Compose, custom local domains, .env configurations, etc.
Check the file release-notes.md.
The Full Stack FastAPI Template is licensed under the terms of the MIT license.






