取り組みの背景 — Antigravity × Python FastAPI の可能性
Python は世界で最も広く使われているプログラミング言語のひとつであり、バックエンド開発においても人気が高まっています。その中でも FastAPI は、型安全・高パフォーマンス・自動ドキュメント生成という3つの特長から、多くの開発者に選ばれている非同期 Web フレームワークです。
Antigravity の AI エージェントとこの FastAPI を組み合わせると、API の設計から実装・テスト・デプロイまでを驚くほど速く進めることができます。ここではAntigravity を活用して Python FastAPI バックエンドを構築する実践的な手順を、具体的なコード例とともに解説します。
この記事で学べること:
- Antigravity で Python FastAPI プロジェクトをゼロから立ち上げる方法
- AI エージェントへの効果的な指示の書き方
- CRUD エンドポイント・JWT 認証・データベース連携の実装パターン
- Cloudflare Workers / Railway / Render へのデプロイ手順
対象読者は Python の基礎文法を理解している方で、Antigravity の基本操作(Planning Mode・エージェント起動)を一度でも試したことがある方を想定しています。
環境準備 — Python + FastAPI + Antigravity のセットアップ
必要なツール
開発を始める前に、以下のツールが揃っていることを確認してください。
- Python 3.11 以上(
python --versionで確認) - pip または uv(高速パッケージマネージャー)
- Antigravity(最新版)
- Git
プロジェクトの初期化
まずターミナルで新しいディレクトリを作成し、Antigravity でプロジェクトを開きます。
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]Antigravity でプロジェクトを開いたら、まず Planning Mode でプロジェクト全体の構造を設計してもらいましょう。チャットに以下のように入力します:
このディレクトリに Python FastAPI プロジェクトを作成してください。
機能: ユーザー管理 CRUD、JWT 認証、SQLite データベース連携。
ファイル構成も提案してください。
Antigravity のエージェントが以下のようなディレクトリ構造を提案してくれます:
my-fastapi-app/
├── app/
│ ├── __init__.py
│ ├── main.py # FastAPI アプリのエントリーポイント
│ ├── models.py # SQLModel / Pydantic モデル定義
│ ├── database.py # DB 接続・セッション管理
│ ├── auth.py # JWT 認証ロジック
│ └── routers/
│ ├── users.py # ユーザー関連エンドポイント
│ └── items.py # アイテム関連エンドポイント
├── tests/
│ └── test_main.py
├── requirements.txt
└── README.md
CRUD エンドポイントの実装
モデルと DB の定義
Antigravity のエージェントに app/models.py と app/database.py を生成してもらいます。チャットに指示します:
app/models.py に SQLModel を使ったユーザーモデルを作成してください。
フィールド: id (int, primary key), username (str), email (str), hashed_password (str), created_at (datetime)
生成されたコードは以下のようになります:
# 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 # 登録時に受け取る平文パスワード
class UserRead(UserBase):
id: int
created_at: datetime続けて 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():
"""起動時にテーブルを作成"""
SQLModel.metadata.create_all(engine)
def get_session():
"""FastAPI の Depends で使う DB セッションジェネレーター"""
with Session(engine) as session:
yield sessionユーザー CRUD エンドポイント
次に app/routers/users.py を実装します。Antigravity に次のように依頼します:
app/routers/users.py にユーザーの CRUD エンドポイントを作成してください。
GET /users (一覧), GET /users/{id} (詳細), POST /users (作成), DELETE /users/{id} (削除)
パスワードは bcrypt でハッシュ化して保存してください。
# 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 session.exec(select(User)).all()
@router.get("/{user_id}", response_model=UserRead)
def get_user(user_id: int, session: Session = Depends(get_session)):
"""指定 ID のユーザーを返す"""
user = session.get(User, user_id)
if not user:
raise HTTPException(status_code=404, detail="ユーザーが見つかりません")
return user
@router.post("/", response_model=UserRead, status_code=201)
def create_user(user_data: UserCreate, session: Session = Depends(get_session)):
"""新規ユーザーを作成する"""
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)):
"""指定 ID のユーザーを削除する"""
user = session.get(User, user_id)
if not user:
raise HTTPException(status_code=404, detail="ユーザーが見つかりません")
session.delete(user)
session.commit()JWT 認証の実装
セキュアな API には認証が欠かせません。Antigravity に JWT 認証の実装を依頼しましょう:
app/auth.py に JWT トークンの発行・検証ロジックを実装してください。
ログインエンドポイント 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):
"""JWT アクセストークンを生成する"""
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:
"""現在ログイン中のユーザーを取得する"""
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="認証情報が無効です",
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),
):
"""ユーザー名とパスワードで JWT トークンを発行する"""
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="ユーザー名またはパスワードが間違っています",
)
token = create_access_token(
data={"sub": user.username},
expires_delta=timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES),
)
return {"access_token": token, "token_type": "bearer"}エントリーポイントの統合
最後に app/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):
"""起動時に DB テーブルを作成する"""
create_db_and_tables()
yield
app = FastAPI(
title="My FastAPI App",
description="Antigravity × Python FastAPI サンプル",
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!"}サーバーを起動して確認します:
uvicorn app.main:app --reload
# → http://localhost:8000/docs にアクセスすると自動生成ドキュメントが表示される期待する出力: INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) と表示され、ブラウザで Swagger UI が確認できます。
Antigravity エージェントを使ったテスト生成
実装が完了したら、Antigravity にテストを自動生成してもらいましょう。チャットに次のように入力します:
tests/test_main.py に pytest + httpx を使ったテストを作成してください。
テスト対象: ユーザー作成・一覧取得・JWT ログイン
# 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
from app.models import User
# テスト用インメモリ DB を使用
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):
"""ユーザー作成が成功することを確認する"""
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 # パスワードが露出しないことを確認
def test_login(client: TestClient):
"""JWT トークンの発行が成功することを確認する"""
# まずユーザーを作成
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()テストを実行します:
pip install pytest httpx
pytest tests/ -v
# 期待する出力: 2 passed in 0.XXsデプロイ — Railway / Render への公開
Railway でのデプロイ
Railway は Python バックエンドのデプロイが数ステップで完了する便利なプラットフォームです。Antigravity に次のように依頼します:
Railway へのデプロイ用に Procfile と requirements.txt を更新してください。
また、環境変数 SECRET_KEY を安全に扱えるように .env.example も作成してください。
Antigravity が生成するファイル群:
# 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
デプロイ手順:
# Railway CLI のインストール
npm install -g @railway/cli
# ログインとプロジェクト作成
railway login
railway init
# 環境変数を設定
railway variables set SECRET_KEY="$(python -c 'import secrets; print(secrets.token_hex(32))')"
# デプロイ実行
railway upセキュアな API バックエンドの構築には、認証・バリデーション・レート制限の実装パターンを詳しく解説した Antigravity エージェントでセキュアな API バックエンドを構築する — 認証・バリデーション・レート制限の実装パターン もぜひご覧ください。
よくあるエラーと対処法
FastAPI + Antigravity の開発でよく遭遇するエラーとその対処法をまとめます。
エラー1: ImportError: No module named 'app'
# NG: ルートから直接実行
python app/main.py
# OK: uvicorn でモジュールパスを指定
uvicorn app.main:app --reloadエラー2: sqlalchemy.exc.OperationalError: no such table
lifespan 関数内の create_db_and_tables() が呼ばれているか確認してください。もし @app.on_event("startup") を使っている場合は、FastAPI 0.93 以降では lifespan への移行が推奨されています。
# 旧 (非推奨)
@app.on_event("startup")
def startup():
create_db_and_tables()
# 新 (推奨)
@asynccontextmanager
async def lifespan(app: FastAPI):
create_db_and_tables()
yield
app = FastAPI(lifespan=lifespan)エラー3: 422 Unprocessable Entity が返ってくる
リクエストボディの型が一致していない可能性があります。Antigravity に「/docs の Swagger UI を見てエラーの原因を調べてください」と伝えると、自動でバリデーションエラーの詳細を解析してくれます。
まとめ
Antigravity × Python FastAPI の組み合わせは、バックエンド開発の生産性を大きく向上させます。本記事で解説した内容を振り返ります。
- プロジェクト初期化: Planning Mode でディレクトリ構造を設計し、AI エージェントにひな型を生成してもらう
- CRUD 実装:
SQLModel+FastAPIの組み合わせで型安全な CRUD を素早く実装できる - JWT 認証:
python-jose+passlibで堅牢な認証フローを AI エージェントが自動構築 - テスト生成: pytest + httpx のテストコードも Antigravity が自動生成
- デプロイ: Railway や Render へのデプロイも AI の指示に従うだけ
Python バックエンドを
Antigravity の強みは、コードを書くだけでなく「どう設計するか」というアーキテクチャレベルの提案もしてくれる点にあります。ぜひ今日から Python FastAPI の開発に Antigravity を取り入れてみてください。