Who am I?
AIとかよく分からないけど使ってみたい人。
とりあえずClaude Codeで何が出来るのか遊んでみたかった。
誰向けの記事
- ClaudeCodeに課金するか迷ってる人
- 何が出来るようになるか知りたい人
インストール方法は書かないので、期待してる人は回れ右
忙しい人向け要約
- とりあえずProに課金してみたらいい
- お一人様開発だったら便利
- チームでやる場合、これはどう使うんだ?
- 使った事ない言語でも作れるので気になってるものを試すのに
- コマンドを勝手に叩いてくれるのもいい
-
rm -rf /
とかやらないかは今後も見守る必要はあるかも
-
蛇足
- AIが作ったものをAIにレビューさせるなら先人が考えてきた可読性や再利用性って要らなくなってくる?
- 要するに「動けばいいじゃん」のコードが増えるかもしれない?
やってみたこと
FrontとBackそれぞれ開発してみた。
ユーザ作成とログイン機能くらいまで。
環境構築や調べたりする時間も含めて8時間くらい?
8時間で作った機能
- ユーザ作成機能
- ユーザログイン機能
+FlutterやDocker、Python環境も一から作成
Frontend
- Flutter: モバイル・Web対応のクロスプラットフォームフレームワーク
- 前々から気になっていたけど学習さぼってたやつ
Backend (AWS)
- ECS (Elastic Container Service): コンテナオーケストレーション
- とりあえずコンテナを使いたかった
- ここはまだデプロイまで試していない
- Python + FastAPI: 高性能なWeb APIフレームワーク
- コンテナ内ではPythonが動いてる
- こっちは開発経験あり
開発日記
Qiitaに書くつもりが無かったので全然メモってないです。ごめんなさい。
読み飛ばしてOK
API開発
WSL2内でClaudeCodeをインストールすることに成功したので、一発目の命令で
login.pyにログイン機能を実装してください。
コンテナで動かす想定で、pythonでfastAPIを使います。
DBはPostgreSQLです。
と書きました。
すると、
# <DB接続の処理>
# ログイン機能
# ユーザ作成機能
# app = FastAPI()
な構成で、一つのファイルに全部乗せ。
自分の頭の中では分かれるつもりだった↓
─── src
├── app.py # <-- メイン
├── database.py # <-- DB接続
├── api
│ └── login.py # <-- ログイン機能API
├── models
│ └── user.py
├── schema
│ └── user.py
書かれたコードに驚きつつ、「おいおい、これだからAIはよう」という気持ち。
だけど、これで動くんだったら別に問題はないんですよね。
あくまで人間が読んだり、整理したりするための構成やルールであって
どんなにスパゲッティコードになろうとAIは読み解けると思う。
なので、クラス作るだとかAIにとっては人間に合わせてるだけなのかも。
とまぁ、紆余曲折ありつつ、login.pyはこうなりました。
私は一切書いていないです。
"""
B-Link Authentication API
このモジュールはJWT認証を使用したユーザー認証システムを提供します。
以下の機能を含みます:
- ユーザー登録
- ユーザーログイン
- JWT トークン認証
- 保護されたルートへのアクセス制御
"""
from fastapi import HTTPException, Depends, status, APIRouter
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from sqlalchemy.orm import Session
from jose import JWTError, jwt
from datetime import timedelta
import os
from schema.user import User, UserCreate, UserLogin, Token
from database import get_db
from models.user import (
get_user, create_user, authenticate_user,
create_access_token, SECRET_KEY, ALGORITHM, ACCESS_TOKEN_EXPIRE_MINUTES
)
from utils.logging_config import log_api_request, log_api_response, log_authentication_event
# APIルーターの設定
router = APIRouter(
prefix="",
tags=["認証"],
responses={
401: {"description": "認証失敗"},
400: {"description": "不正なリクエスト"}
}
)
# JWT Bearer認証スキーム
security = HTTPBearer()
async def get_current_user(credentials: HTTPAuthorizationCredentials = Depends(security), db: Session = Depends(get_db)):
"""JWTトークンから現在のユーザーを取得します。
Args:
credentials: Bearer認証の認証情報
db: データベースセッション
Returns:
認証されたユーザーオブジェクト
Raises:
HTTPException: 認証に失敗した場合(401 Unauthorized)
"""
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)
try:
token = credentials.credentials
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
user_id: str = payload.get("sub")
if user_id is None:
raise credentials_exception
except JWTError:
raise credentials_exception
user = get_user(db, user_id=user_id)
if user is None:
raise credentials_exception
return user
@router.post("/register", response_model=dict)
async def register_user(user: UserCreate, db: Session = Depends(get_db)):
"""新しいユーザーを登録します。
Args:
user: 登録するユーザー情報(user_idとpassword)
db: データベースセッション
Returns:
登録成功メッセージ
Raises:
HTTPException: ユーザーIDが既に存在する場合(400 Bad Request)
"""
# API リクエストログ出力
log_api_request(
endpoint="/register",
method="POST",
body_data=user.model_dump(),
user_id=None
)
try:
db_user = get_user(db, user_id=user.user_id)
if db_user:
log_authentication_event("register", user.user_id, False, "User already exists")
raise HTTPException(status_code=400, detail="User already registered")
create_user(db=db, user=user)
log_authentication_event("register", user.user_id, True, "User registered successfully")
response = {"message": "User created successfully"}
log_api_response("/register", 200, response)
return response
except HTTPException as e:
log_api_response("/register", e.status_code, {"error": e.detail})
raise
@router.post("/login", response_model=Token)
async def login_for_access_token(user_credentials: UserLogin, db: Session = Depends(get_db)):
"""ユーザーログインしてアクセストークンを取得します。
Args:
user_credentials: ログイン証明情報(user_idとpassword)
db: データベースセッション
Returns:
JWTアクセストークンとトークンタイプ
Raises:
HTTPException: 認証に失敗した場合(401 Unauthorized)
"""
# API リクエストログ出力
log_api_request(
endpoint="/login",
method="POST",
body_data=user_credentials.model_dump(),
user_id=user_credentials.user_id
)
try:
user = authenticate_user(db, user_credentials.user_id, user_credentials.password)
if not user:
log_authentication_event("login", user_credentials.user_id, False, "Invalid credentials")
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect user ID or password",
headers={"WWW-Authenticate": "Bearer"},
)
access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
access_token = create_access_token(
data={"sub": user.user_id}, expires_delta=access_token_expires
)
log_authentication_event("login", user.user_id, True, "Login successful")
response = {"access_token": access_token, "token_type": "bearer"}
log_api_response("/login", 200, response)
return response
except HTTPException as e:
log_api_response("/login", e.status_code, {"error": e.detail})
raise
@router.get("/protected")
async def protected_route(current_user: User = Depends(get_current_user)):
"""認証が必要な保護されたルートです。
Args:
current_user: 現在の認証されたユーザー
Returns:
ユーザーへの挨拶メッセージ
"""
return {"message": f"Hello {current_user.user_id}, this is a protected route!"}
注目ポイント
- JWT(Json Web Token)は勝手に選択された
- コンテナで運用するという命令からJWTになったっぽい
- 「ログイン機能」という命令だけでユーザ作成もちゃんと実装された
- docstringもほとんどなかったので、そこはgoogle式で書いてくれって命令した
Flutter
エンドポイントが出来ましたんで、それを元にログイン画面つくってくれ、と指示しました。
├── b-link # <-- APIね
├── b-link-front # <-- Flutter。ここにいる
お隣さんのディレクトリのsrcディレクトリを読んで作ってね、と。
で、出来たのがこちら。
注目ポイント
- アイコンの指定とかはなく、勝手にやってくれた
- mainとビジネスロジック、画面は分かれて実装された
メテオフォール!!
私は神なので、急な仕様変更だって無情にも実施します。
fastAPIに飽きました。これからの時代はgRPCです。
こんなことを言われたらキレ散らかすと思いますがAIは怒りません。
アンガーマネジメント出来てて偉い
簡単に移行してくれるかと思ったんですが、そこはやはり難しいみたい?
Flutter側でエラーが出てうまく接続できてません。
→ WebUIで開発していたので、間にプロキシサーバが必要だった模様。ネイティブアプリとして作成する分にはOK。
メテオフォールはほどほどに。。
まとめ(雑感)
やりたい事が明確になっていて、自分でテストを書ける人(モンキーテストでもいいか)は試してみる価値はあると思います。
アーキテクチャや言語の選択が出来るような、アンテナの感度はある程度あった方がより楽しいかもしれません。
その辺も質問しながら実施していっても良いでしょうね。
急な仕様変更でもある程度対応出来てたので、「今からその仕様変更はつらい…」という事が言いづらくなる世界がくるのかも(怖い)