Setup and context — Why Antigravity + Python FastAPI?
Python consistently ranks among the most widely used programming languages, and its popularity in backend development continues to grow. Within that ecosystem, FastAPI has become a go-to framework thanks to its three standout traits: type safety, high performance, and automatic API documentation.
Combine FastAPI with Antigravity's AI agents and you can move from idea to deployed API at a pace that previously required an entire team. This guide walks you through building a complete FastAPI backend — CRUD endpoints, JWT authentication, database integration, and cloud deployment — using Antigravity as your AI coding partner.
What you'll learn:
- Bootstrapping a Python FastAPI project from zero with Antigravity
- Writing effective prompts to get the most out of AI agents
- Implementing CRUD endpoints, JWT auth, and SQLite/PostgreSQL integration
- Deploying to Cloudflare Workers, Railway, or Render
This guide assumes you know basic Python syntax and have tried Antigravity at least once (Planning Mode or agent chat).
Setup — Python + FastAPI + Antigravity
Prerequisites
Make sure the following tools are installed before you start:
- Python 3.11 or later (
python --version) - pip or uv for fast package management
- Antigravity (latest version)
- Git
Project Initialization
Create a new directory, set up a virtual environment, and install the core dependencies:
mkdir my-fastapi-app && cd my-fastapi-app
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install fastapi uvicorn[standard] sqlmodel pydantic python-jose[cryptography]Open the project in Antigravity, then switch to Planning Mode and prompt it to scaffold the architecture:
Create a Python FastAPI project in this directory.
Features: user management CRUD, JWT authentication, SQLite integration.
Please suggest the folder structure as well.
Antigravity will propose a structure like this:
my-fastapi-app/
├── app/
│ ├── __init__.py
│ ├── main.py # FastAPI application entry point
│ ├── models.py # SQLModel / Pydantic models
│ ├── database.py # DB connection and session management
│ ├── auth.py # JWT authentication logic
│ └── routers/
│ ├── users.py # User-related endpoints
│ └── items.py # Item-related endpoints
├── tests/
│ └── test_main.py
├── requirements.txt
└── README.md
Implementing CRUD Endpoints
Defining Models and the Database
Ask Antigravity to generate app/models.py and app/database.py:
Create a user model in app/models.py using SQLModel.
Fields: id (int, primary key), username (str), email (str), hashed_password (str), created_at (datetime)
The generated code looks like this:
# app/models.py
from datetime import datetime
from typing import Optional
from sqlmodel import Field, SQLModel
class UserBase(SQLModel):
username: str = Field(index=True, unique=True)
email: str = Field(index=True, unique=True)
class User(UserBase, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
hashed_password: str
created_at: datetime = Field(default_factory=datetime.utcnow)
class UserCreate(UserBase):
password: str # Plain-text password received at registration
class UserRead(UserBase):
id: int
created_at: datetimeNext, generate app/database.py:
# app/database.py
from sqlmodel import create_engine, Session, SQLModel
DATABASE_URL = "sqlite:///./app.db"
engine = create_engine(DATABASE_URL, echo=True)
def create_db_and_tables():
"""Create tables on startup."""
SQLModel.metadata.create_all(engine)
def get_session():
"""DB session generator used with FastAPI's Depends."""
with Session(engine) as session:
yield sessionUser CRUD Router
Prompt Antigravity to implement the user router:
Create CRUD endpoints for users in app/routers/users.py.
Routes: GET /users, GET /users/{id}, POST /users, DELETE /users/{id}
Hash passwords with bcrypt before saving.
# app/routers/users.py
from fastapi import APIRouter, Depends, HTTPException
from sqlmodel import Session, select
from passlib.context import CryptContext
from app.database import get_session
from app.models import User, UserCreate, UserRead
router = APIRouter(prefix="/users", tags=["users"])
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
@router.get("/", response_model=list[UserRead])
def list_users(session: Session = Depends(get_session)):
"""Return all users."""
return session.exec(select(User)).all()
@router.get("/{user_id}", response_model=UserRead)
def get_user(user_id: int, session: Session = Depends(get_session)):
"""Return a user by ID."""
user = session.get(User, user_id)
if not user:
raise HTTPException(status_code=404, detail="User not found")
return user
@router.post("/", response_model=UserRead, status_code=201)
def create_user(user_data: UserCreate, session: Session = Depends(get_session)):
"""Create a new user."""
hashed_pw = pwd_context.hash(user_data.password)
user = User(
username=user_data.username,
email=user_data.email,
hashed_password=hashed_pw,
)
session.add(user)
session.commit()
session.refresh(user)
return user
@router.delete("/{user_id}", status_code=204)
def delete_user(user_id: int, session: Session = Depends(get_session)):
"""Delete a user by ID."""
user = session.get(User, user_id)
if not user:
raise HTTPException(status_code=404, detail="User not found")
session.delete(user)
session.commit()JWT Authentication
No production API is complete without authentication. Ask Antigravity to add JWT auth:
Implement JWT token generation and validation in app/auth.py.
Also add a login endpoint: POST /auth/token
# app/auth.py
import os
from datetime import datetime, timedelta
from typing import Optional
from fastapi import APIRouter, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from jose import JWTError, jwt
from passlib.context import CryptContext
from sqlmodel import Session, select
from app.database import get_session
from app.models import User
SECRET_KEY = os.environ.get("SECRET_KEY", "change-this-in-production")
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 60
router = APIRouter(prefix="/auth", tags=["auth"])
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/auth/token")
def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
"""Generate a JWT access token."""
to_encode = data.copy()
expire = datetime.utcnow() + (expires_delta or timedelta(minutes=15))
to_encode.update({"exp": expire})
return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
async def get_current_user(
token: str = Depends(oauth2_scheme),
session: Session = Depends(get_session),
) -> User:
"""Retrieve the currently authenticated user."""
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid credentials",
headers={"WWW-Authenticate": "Bearer"},
)
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
username: str = payload.get("sub")
if username is None:
raise credentials_exception
except JWTError:
raise credentials_exception
user = session.exec(select(User).where(User.username == username)).first()
if user is None:
raise credentials_exception
return user
@router.post("/token")
def login(
form_data: OAuth2PasswordRequestForm = Depends(),
session: Session = Depends(get_session),
):
"""Issue a JWT token for valid username/password credentials."""
user = session.exec(select(User).where(User.username == form_data.username)).first()
if not user or not pwd_context.verify(form_data.password, user.hashed_password):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect username or password",
)
token = create_access_token(
data={"sub": user.username},
expires_delta=timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES),
)
return {"access_token": token, "token_type": "bearer"}Wiring Everything Together in main.py
# app/main.py
from contextlib import asynccontextmanager
from fastapi import FastAPI
from app.database import create_db_and_tables
from app.routers import users
from app import auth
@asynccontextmanager
async def lifespan(app: FastAPI):
"""Create DB tables on startup."""
create_db_and_tables()
yield
app = FastAPI(
title="My FastAPI App",
description="Antigravity × Python FastAPI sample",
version="1.0.0",
lifespan=lifespan,
)
app.include_router(users.router)
app.include_router(auth.router)
@app.get("/")
def root():
return {"message": "Hello, FastAPI + Antigravity!"}Start the server and verify everything works:
uvicorn app.main:app --reload
# → Visit http://localhost:8000/docs to see the auto-generated Swagger UIExpected output: INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
AI-Assisted Test Generation
Once the implementation is in place, let Antigravity write the tests:
Create pytest + httpx tests in tests/test_main.py.
Cover: user creation, user listing, and JWT login.
# tests/test_main.py
import pytest
from fastapi.testclient import TestClient
from sqlmodel import create_engine, Session, SQLModel
from app.main import app
from app.database import get_session
TEST_DATABASE_URL = "sqlite://"
@pytest.fixture(name="session")
def session_fixture():
engine = create_engine(TEST_DATABASE_URL, connect_args={"check_same_thread": False})
SQLModel.metadata.create_all(engine)
with Session(engine) as session:
yield session
@pytest.fixture(name="client")
def client_fixture(session: Session):
def override_get_session():
yield session
app.dependency_overrides[get_session] = override_get_session
yield TestClient(app)
app.dependency_overrides.clear()
def test_create_user(client: TestClient):
"""Verify that user creation returns 201 and the correct data."""
response = client.post("/users/", json={
"username": "testuser",
"email": "test@example.com",
"password": "securepassword123"
})
assert response.status_code == 201
data = response.json()
assert data["username"] == "testuser"
assert "hashed_password" not in data # Confirm password is not exposed
def test_login(client: TestClient):
"""Verify that a valid login returns a JWT access token."""
client.post("/users/", json={
"username": "loginuser",
"email": "login@example.com",
"password": "password123"
})
response = client.post("/auth/token", data={
"username": "loginuser",
"password": "password123"
})
assert response.status_code == 200
assert "access_token" in response.json()Run the tests:
pip install pytest httpx
pytest tests/ -v
# Expected: 2 passed in 0.XXsDeployment — Shipping to Railway or Render
Deploying to Railway
Railway makes Python backend deployment straightforward. Ask Antigravity to prepare the deployment files:
Update the Procfile and requirements.txt for Railway deployment.
Also create a .env.example to handle the SECRET_KEY safely.
Generated files:
# Procfile
web: uvicorn app.main:app --host 0.0.0.0 --port $PORT
# .env.example
SECRET_KEY=your-secret-key-here
DATABASE_URL=sqlite:///./app.db
Deploy in a few commands:
npm install -g @railway/cli
railway login
railway init
railway variables set SECRET_KEY="$(python -c 'import secrets; print(secrets.token_hex(32))')"
railway upTo take your API security further, check out Antigravity エージェントでセキュアな API バックエンドを構築する — 認証・バリデーション・レート制限の実装パターン, which dives deep into authentication patterns, request validation, and rate limiting strategies.
Common Errors and Fixes
Here are the most frequent issues you'll encounter when building FastAPI apps with Antigravity, along with their solutions.
Error 1: ImportError: No module named 'app'
# Wrong — running the module directly
python app/main.py
# Correct — use uvicorn with the module path
uvicorn app.main:app --reloadError 2: sqlalchemy.exc.OperationalError: no such table
Check that create_db_and_tables() is being called inside the lifespan context. If your code uses the deprecated @app.on_event("startup") decorator, migrate to the lifespan approach supported in FastAPI 0.93+.
Error 3: 422 Unprocessable Entity
A request body type mismatch is the usual culprit. Tell Antigravity: "Look at the Swagger UI at /docs and identify the validation error." The agent will inspect your Pydantic model and highlight the mismatch.
Summary
Pairing Antigravity with Python FastAPI creates a powerful workflow for backend development. Here's a recap of what we covered:
- Project setup: Use Planning Mode to design the folder structure, then let the agent scaffold the boilerplate.
- CRUD implementation: SQLModel + FastAPI delivers type-safe CRUD endpoints with minimal code.
- JWT authentication:
python-jose+passlibprovide production-ready auth that Antigravity can generate in seconds. - Test generation: pytest + httpx test suites are written automatically by the agent.
- Deployment: Railway and Render deployments are guided step-by-step by the AI.
Antigravity's real strength lies not just in generating code, but in proposing architecture — the how behind the what. Give it a try on your next Python FastAPI project today.