Building a FastAPI + GraphQL Starter Template with PostgreSQL: Part 1
Introduction
In this tutorial, we’ll build a production-ready FastAPI application with GraphQL support and PostgreSQL integration from scratch. This first part covers:
- Setting up a clean project structure
- Async database connectivity with SQLAlchemy & asyncpg
- Basic GraphQL API with Strawberry
- Health check endpoints
- Environment-based configuration
By the end, you’ll have a starter template ready for development and future enhancements.
Prerequisites
Make sure you have:
- Python 3.11+
- PostgreSQL running locally or remotely
- Git for version control
- Basic familiarity with Python
async/await
Step 1: Project Initialization
Create your project folder and initialize git:
mkdir fastapi-graphql-starter-template
cd fastapi-graphql-starter-template
git init
Step 2: Project Structure
A clean folder structure is key for maintainability:
fastapi-graphql-starter-template/
├── app/
│ ├── main.py # FastAPI entry point
│ ├── api/routes/ # REST endpoints (health checks, etc.)
│ ├── core/ # Config, database, logging
│ ├── graphql/ # GraphQL schema, queries, mutations
│ ├── models/ # SQLAlchemy models
│ └── services/ # Business logic
├── migrations/ # Alembic migrations
├── tests/ # Unit and integration tests
├── .env.example # Environment template
├── requirements.in / requirements.txt
├── run.sh # Convenience script to run server
└── README.md
Create the directories:
mkdir -p app/{api/routes,core,graphql,models,services} migrations tests
touch app/{__init__.py,main.py}
touch app/api/routes/__init__.py
touch app/core/{__init__.py,config.py,database.py,logging.py}
touch app/graphql/{__init__.py,schema.py,queries.py,mutations.py}
touch app/models/{__init__.py,user.py}
touch app/services/__init__.py
Step 3: Dependencies and What They Do
Here’s a brief overview of the main dependencies we use:
| Dependency | Purpose | Link |
|---|---|---|
| FastAPI | High-performance Python web framework with async support | Docs |
| Uvicorn | ASGI server to run FastAPI apps | Docs |
| SQLAlchemy | ORM to interact with relational databases; we use async support | Docs |
| asyncpg | Fast async PostgreSQL driver | Docs |
| Alembic | Database migrations for SQLAlchemy models | Docs |
| Strawberry GraphQL | Type-safe GraphQL library for Python | Docs |
| Pydantic | Data validation and settings management | Docs |
| python-dotenv | Load environment variables from .env files |
Docs |
Together, these tools provide a scalable, type-safe, and async-ready FastAPI stack with proper database management and GraphQL support.
Install them with pip-tools:
pip install pip-tools
echo "
fastapi==0.109.0
uvicorn[standard]==0.27.0
sqlalchemy[asyncio]==2.0.25
asyncpg==0.29.0
alembic==1.13.1
strawberry-graphql[fastapi]==0.219.0
pydantic==2.5.3
pydantic-settings==2.1.0
python-dotenv==1.0.0
" > requirements.in
pip-compile requirements.in
pip install -r requirements.txt
Step 4: Configuration Management
Use Pydantic Settings to load environment variables:
from pydantic_settings import BaseSettings
from pydantic import Field
class Settings(BaseSettings):
app_name: str = Field(default="FastAPI GraphQL Starter")
debug: bool = Field(default=True)
environment: str = Field(default="development")
database_url: str = Field(..., env="DATABASE_URL")
class Config:
env_file = ".env"
settings = Settings()
.env.example:
APP_NAME=FastAPI GraphQL Starter
DEBUG=True
ENVIRONMENT=development
DATABASE_URL=postgresql+asyncpg://user:password@localhost:5432/dbname
HOST=0.0.0.0
PORT=8000
Step 5: Async Database Setup
from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker, AsyncSession
from sqlalchemy.orm import declarative_base
from app.core.config import settings
import ssl
Base = declarative_base()
ssl_context = None
if settings.environment == "production":
ssl_context = ssl.create_default_context()
ssl_context.check_hostname = False
ssl_context.verify_mode = ssl.CERT_NONE
engine = create_async_engine(
settings.database_url,
echo=settings.debug,
future=True,
connect_args={"ssl": ssl_context} if ssl_context else {}
)
AsyncSessionLocal = async_sessionmaker(
engine,
class_=AsyncSession,
expire_on_commit=False
)
- SQLAlchemy manages models and sessions
- asyncpg handles fast asynchronous PostgreSQL connections
- SSL support configurable for production environments
Step 6: Create Your First Model
from sqlalchemy import Column, Integer, String, DateTime, func
from app.core.database import Base
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True, index=True)
email = Column(String, unique=True, nullable=False)
username = Column(String, unique=True, nullable=False)
hashed_password = Column(String, nullable=False)
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
Step 7: Database Migrations with Alembic
alembic init migrations
Configure env.py for async migrations (import all models for autogenerate):
from app.core.database import Base
import app.models.user
target_metadata = Base.metadata
Create your first migration:
alembic revision --autogenerate -m "Initial migration with User model"
alembic upgrade head
Step 8: GraphQL with Strawberry
import strawberry
@strawberry.type
class Query:
@strawberry.field
def hello(self) -> str:
return "Hello from GraphQL!"
- Integrate with FastAPI via
GraphQLRouter - Supports queries, mutations, and subscriptions
Step 9: FastAPI Application Setup
from fastapi import FastAPI
from strawberry.fastapi import GraphQLRouter
from app.core.config import settings
from app.core.database import init_db, close_db
from app.graphql.schema import schema
from app.api.routes.health import router as health_router
app = FastAPI(title=settings.app_name, debug=settings.debug)
# REST health check
app.include_router(health_router, prefix="/api", tags=["health"])
# GraphQL endpoint
graphql_app = GraphQLRouter(schema)
app.include_router(graphql_app, prefix="/graphql")
@app.get("/")
async def root():
return {"message": "FastAPI GraphQL Starter", "docs": "/docs", "graphql": "/graphql"}
Step 10: Running the Application
Create run.sh:
#!/bin/bash
uvicorn app.main:app --reload --host 0.0.0.0 --port 8000
chmod +x run.sh
./run.sh
Visit:
- API Docs: http://localhost:8000/docs
- GraphQL Playground: http://localhost:8000/graphql
- Health Check: http://localhost:8000/api/health
Next Steps (Part 2)
- JWT authentication (access + refresh tokens)
- Password hashing (bcrypt)
- User registration/login mutations
- Role-based access control
- Protected GraphQL queries and mutations
Conclusion
You now have a solid foundation:
- ✅ Clean, scalable project structure
- ✅ Async PostgreSQL integration with SQLAlchemy + asyncpg
- ✅ Alembic migrations configured
- ✅ GraphQL API with Strawberry
- ✅ Health monitoring endpoint
- ✅ Environment-based configuration
The complete code is available on GitHub.
Key Takeaways:
- Proper project structure prevents technical debt
- Async/await improves performance
- Type-safe GraphQL APIs with Strawberry
- Alembic manages database schema evolution
Stay tuned for Part 2, where we’ll add authentication, security, and protected GraphQL queries!
Top comments (0)