Open22

FastAPI開発入門_ハンズオンのメモ+Python基礎知識のメモ

ぬぬぬぬ

🧭 1. 開発スタイルの変遷と Dev Container の導入

🧱 以前の開発スタイル(Docker Compose ベース)

  • ホスト側(WSL2) でコードを編集し、Docker コンテナ で実行。
  • docker-compose.yaml でボリュームマウントを設定し、ホストとコンテナ間でコードを共有。
  • poetry を使用して依存関係を管理し、仮想環境 .venv をプロジェクト内に作成。

このスタイルでは、ホストとコンテナの環境差異や依存関係の管理が課題となることがありました。

🧰 Dev Container の導入

  • VS Code の Dev Containers 拡張機能 を使用して、開発環境をコンテナ化。
  • プロジェクト内に .devcontainer ディレクトリを作成し、devcontainer.jsonDockerfile を配置。
  • 開発環境の構成をコードとして管理し、チーム全体で統一された環境を提供。

これにより、環境構築の手間が軽減され、環境差異による問題も減少しました。


🧪 2. Dev Container の構成と起動

📁 .devcontainer ディレクトリの構成

  • devcontainer.json: 開発環境の設定ファイル。使用する Docker イメージや拡張機能、起動時のコマンドなどを定義。
  • Dockerfile: 必要に応じてカスタムイメージを作成するためのファイル。(エンベーダー)

🚀 Dev Container の起動手順

  1. VS Code でプロジェクトを開く。
  2. 左下の「><」アイコンをクリックし、「Reopen in Container」を選択。
  3. 初回起動時は、必要なイメージのビルドや依存関係のインストールが自動で行われる。(エンベーダー, Zenn)

これにより、開発環境がコンテナ内に構築され、VS Code 上でシームレスに開発が可能となります。


🧩 3. Dev Container の利点と注意点

✅ 利点

  • 環境の一貫性: チーム全体で同一の開発環境を共有できる。
  • 環境構築の簡素化: 新しい開発者も迅速に環境を構築可能。
  • ローカル環境の汚染防止: ホストマシンに依存関係をインストールする必要がない。(Zenn, ayuina.github.io)

⚠️ 注意点

  • パフォーマンス: 一部の操作でパフォーマンスに影響が出る場合がある。
  • 学習コスト: 初めて使用する場合、設定や操作方法の習得が必要。

🔚 4. Dev Container の停止方法

🛑 VS Code からの停止

  1. 左下の「><」アイコンをクリック。
  2. 「Reopen Folder Locally」を選択。(Zenn)

これにより、コンテナが停止し、ローカル環境に戻ります。

🔧 コマンドラインからの停止

docker ps  # 実行中のコンテナを確認
docker stop <コンテナ名>  # コンテナの停止
docker rm <コンテナ名>    # コンテナの削除(必要に応じて)

📚 5. 参考リンク

ぬぬぬぬ

🏗️ 現在のプロジェクト構成(整理)

PYTHON-FASTAPI/
├── .devcontainer/             # Dev Container 設定ディレクトリ
│   ├── devcontainer.json      # コンテナの定義
│   └── Dockerfile             # 開発環境用のDockerfile
├── .venv/                     # poetry が自動生成した仮想環境(in-project)
├── api/                       # FastAPI アプリケーション
│   ├── __init__.py
│   └── main.py                # エントリポイント(FastAPI app)
├── docker-compose.yaml        # ← Dev Containerでは未使用(説明あり)
├── poetry.lock                # インストールされた依存のバージョン情報
└── pyproject.toml             # プロジェクト定義と依存管理(Poetry)

🪄 環境構築フロー(7ステップ)


✅ STEP 1️⃣: VS Codeで「Reopen in Container」

  • VS Code左下 ><Reopen in Container を選択
  • .devcontainer/devcontainer.json を読み取り
  • Dockerfile で開発用コンテナをビルド開始

✅ STEP 2️⃣: Dockerfile による環境構築

Dockerfile の役割:

FROM python:3.11-buster
WORKDIR /src
RUN pip install poetry
COPY pyproject.toml poetry.lock* ./
RUN poetry config virtualenvs.in-project true
RUN poetry install --no-root
  • /src/.venv に仮想環境を構築
  • pyproject.toml の内容から依存をインストール(FastAPI など)

✅ STEP 3️⃣: devcontainer.json の実行設定

{
  "postCreateCommand": "poetry install --no-root",
  "postStartCommand": "poetry run uvicorn api.main:app --host 0.0.0.0 --reload",
  "forwardPorts": [8000]
}
  • postCreateCommand → コンテナ作成後に Poetry で依存解決を再確認
  • postStartCommand → コンテナ起動後に uvicorn を自動実行
  • forwardPortslocalhost:8000 を自動でホストへ接続可能に

✅ STEP 4️⃣: .venv の仮想環境が自動で選択される

  • poetry の設定 virtualenvs.in-project = true により .venv/ が生成される
  • VS Code 側が .venv/bin/python を自動で補完対象に認識✨

✅ STEP 5️⃣: FastAPI アプリが自動起動!

poetry run uvicorn api.main:app --host 0.0.0.0 --reload
  • http://localhost:8000/docs でSwagger UIが表示
  • .venv 経由でライブラリが正しく読み込まれる

✅ STEP 6️⃣: コード変更が即反映(ホットリロード)

  • VS Code で api/main.py を編集
  • --reload により uvicorn が自動で再読み込み

✅ STEP 7️⃣: docker-compose.yaml は現時点では未使用

  • 現状は シングルサービスの開発環境
  • 将来的に PostgreSQL や Redis を追加したい場合には docker-compose.yaml に役割が出てきます

💡 こうして整った開発環境は…

Docker コンテナに完全分離されたPython環境
VS Codeの補完・Lintが .venv に完全対応
FastAPIアプリが起動時に自動実行
どこでも・誰でも同じ環境で開発可能(コード + 環境の持ち運び)

ぬぬぬぬ

poetry run uvicorn api.main:app --host 0.0.0.0 --reload の意味をLaravel経験者向けに解説

FastAPI を開発中に登場するこのコマンド、一見すると複雑に見えますが、Laravel経験者にとっては馴染みのある考え方と対応しています。本記事では、各オプションが何を意味するか、Laravelとの対比も交えて解説します。

🎯 コマンドの全体像

poetry run uvicorn api.main:app --host 0.0.0.0 --reload
パーツ 意味 Laravel類似
poetry run 仮想環境内でコマンド実行 .env 読み込み後の php artisan
uvicorn 非同期対応のASGIサーバ php -S もしくは Laravel Sail
api.main:app api/main.pyapp を指定 Route::get() でのコントローラ指定
--host 0.0.0.0 外部アクセス許可 host=0.0.0.0.env に記述する場合に相当
--reload ファイル変更時に自動再起動 Laravelには標準では無し(BrowserSync等が類似)

🧪 最小構成のFastAPIアプリ

# ファイル: api/main.py

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
def read_root():
    """ルートエンドポイント"""
    return {"message": "こんにちは、FastAPI!"}

🤔 よくある質問

Q1. --host 0.0.0.0 は何のため?

外部ネットワーク(Docker・他のPCなど)からアクセス可能にするためです。開発中は必要なケースが多いです。

Q2. api.main:app の意味は?

Python的には from api.main import app と同じ。対象のFastAPIアプリケーションオブジェクトを指定しています。

Q3. --reload は本番で使える?

いいえ、開発用です。本番では無効化し、代わりに gunicornuvicorn --workers を使いましょう。

🚀 Laravelとの対比まとめ

FastAPI / Poetry Laravel
poetry run php artisan
uvicorn PHPの内蔵サーバ(php -S)やLaravel Sail
app = FastAPI() Route::get() でのルーティング登録
--reload 自動リロード機能(Laravelでは別途ツールが必要)

🔗 参考リンク

ぬぬぬぬ

REST API 入門【FastAPI × Laravel 比較で理解する】

REST API とは?

REST(REpresentational State Transfer)は、Web 上の資源(リソース)を操作するための設計原則です。REST API はこの原則に従った Web API のことを指します。

特徴

  • リソースベース:URL で資源を一意に表現
  • HTTPメソッドで操作を明示:GET, POST, PUT, DELETEなど
  • ステートレス:サーバーはセッション状態を保持しない
  • JSON形式での通信が一般的

HTTPメソッドと対応する操作

HTTPメソッド 意味 Laravel FastAPI
GET 読み込み Route::get('/users/{id}') @app.get('/users/{user_id}')
POST 作成 Route::post('/users') @app.post('/users/')
PUT/PATCH 更新 Route::put('/users/{id}') @app.put('/users/{user_id}')
DELETE 削除 Route::delete('/users/{id}') @app.delete('/users/{user_id}')

FastAPI での REST API 実装例

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

class User(BaseModel):
    name: str
    email: str

fake_users_db = {}

@app.post("/users/")
def create_user(user: User):
    """ユーザーを作成"""
    user_id = len(fake_users_db) + 1
    fake_users_db[user_id] = user
    return {"id": user_id, "user": user}

@app.get("/users/{user_id}")
def read_user(user_id: int):
    """ユーザーを取得"""
    return {"user": fake_users_db.get(user_id)}

よくある質問

Q1. REST API の「リソース」って何?
A. ユーザーや商品など、Web 上で操作したい「モノ」のことです。

Q2. Laravel と FastAPI の対応関係は?
A. ルーティング、コントローラ、リクエストバリデーションに相当する機能が FastAPI にもあります。

Q3. ステートレスって何?
A. 各リクエストが完全に独立しており、サーバー側で過去の状態を覚えていない設計です。


参考リンク

ぬぬぬぬ

FastAPI における APIRouter の使い方と必要性

APIRouter とは?

APIRouterルーティングの分割・再利用のためのクラスです。
FastAPI アプリ全体をモジュールごとに整理したいときに用います。


なぜ APIRouter を使うのか?

主な目的

目的 内容
分割管理 ルーティングをファイル単位・機能単位に分けて整理できる
再利用性 複数のアプリやマイクロサービスで同じルーティングを再利用可能
一元登録 まとめて app.include_router() で読み込むことで、構造がスッキリ

Laravel に例えると?

Laravel における Route::group()コントローラ単位のルート定義に相当します:

// Laravel例
Route::prefix('tasks')->group(function () {
    Route::get('/', 'TaskController@index');
    Route::post('/', 'TaskController@store');
});

FastAPI ではこれをモジュール化して整理します:

# tasks.py
from fastapi import APIRouter

router = APIRouter()

@router.get("/tasks")
async def list_tasks():
    return {"message": "list"}

# main.py
from fastapi import FastAPI
from tasks import router as tasks_router

app = FastAPI()
app.include_router(tasks_router)

よくある質問(FAQ)

Q1. @app.get ではだめなの?
A1. 小規模なアプリなら問題ありませんが、大規模になるとルートが肥大化して管理が難しくなります。

Q2. APIRouter はどこに配置するのがベスト?
A2. 各機能(例:users.py, tasks.py)に分割し、main.py でまとめて include_router するのがベストです。

Q3. ルーティングに prefix を付けられる?
A3. はい、include_router(router, prefix="/tasks") のようにすれば、全ルートに /tasks が付きます。


まとめ:APIRouter のメリット

  • ✅ コードの可読性・保守性が向上
  • ✅ モジュール単位での開発分担が容易
  • ✅ Laravel 経験者にとっては Route::group() に似ていて親しみやすい

参考リンク

ぬぬぬぬ

FastAPI の「スキーマ」とは何か?〜pydantic と一緒に理解しよう〜

はじめに

FastAPI を使う上で頻出する用語が「スキーマ(schema)」です。
これは データ構造とバリデーションルールを定義するクラスを指し、その中核を担っているのが pydantic というライブラリです。


スキーマとは何か?

FastAPI における「スキーマ」とは、主に以下の目的で使われる構造定義です:

  • リクエストボディのバリデーション(入力チェック)
  • レスポンスデータの構造定義(出力整形)
  • 自動ドキュメント(Swagger UI)の元データ

このスキーマは pydantic.BaseModel を継承することで作られます。


Laravel との対比

役割 Laravel FastAPI
入力バリデーション FormRequest / $request->validate() BaseModel
出力整形(DTO) Resource / DTO クラス BaseModel
自動ドキュメント 手動 or Laravel API Resource Doc 自動(OpenAPI)

実例:スキーマの定義と利用

from pydantic import BaseModel, Field

# リクエスト用スキーマ
class TaskCreate(BaseModel):
    title: str = Field(..., min_length=1)
    description: str = Field(..., max_length=255)

# レスポンス用スキーマ
class TaskOut(BaseModel):
    id: int
    title: str
    description: str

使用例(FastAPI 側):

from fastapi import FastAPI

app = FastAPI()

@app.post("/tasks", response_model=TaskOut)
async def create_task(task: TaskCreate):
    return TaskOut(id=1, **task.dict())

pydantic とは?

pydantic は Python 製のデータ検証ライブラリです。FastAPI のスキーマはこのライブラリを土台にしています。

特徴

  • Python の型ヒントに基づく自動バリデーション
  • 不正な入力に対しては詳細な例外(ValidationError)を返す
  • .dict().json() によるシリアライズが簡単
  • FastAPI に組み込まれており、ドキュメントやレスポンスの一貫性を保てる

よくある質問(FAQ)

Q1. ORM(例:SQLAlchemy)とスキーマの違いは?
A1. ORM は DB モデル、スキーマは API 通信用のデータ構造。責務が違います。

Q2. リクエスト用・レスポンス用は分けるべき?
A2. はい、ID などのフィールドが異なるため、明確に分離するのがベストプラクティスです。

Q3. バリデーションルールはどこに書く?
A3. Field() の引数に min_length, max_length, ge(>=)などを指定します。


おわりに

FastAPI におけるスキーマ設計は、Laravel の FormRequest と DTO を組み合わせたようなものであり、さらに pydantic によって 自動バリデーション+自動ドキュメント化 という大きな利点が加わります。


参考リンク

ぬぬぬぬ

🐍 FastAPI × Python型ヒント × スキーマの基礎まとめ

🎯 本記事の目的

FastAPI を学ぶ上で欠かせない「型ヒント」と「スキーマ(Pydanticモデル)」の基礎知識を、Laravelとの対比も交えて整理します。


📌 Python の型ヒントとは?

Python は動的型付け言語ですが、3.5以降では「型ヒント」によってデータ型を明示できます。

def greet(name: str) -> str:
    return f"Hello, {name}"

✅ よく使う型ヒントの例

型ヒント 意味
int, str, bool 基本型
List[str] 文字列のリスト(Python 3.8以前)
list[str] 文字列のリスト(Python 3.9以降)
Dict[str, int] キーが文字列、値が整数の辞書
Union[str, int] 文字列または整数
List[Union[str, int]] 文字列と整数が混在するリスト

🚀 FastAPI におけるスキーマとは?

FastAPI では Pydantic を使って「受け渡すデータの構造(=スキーマ)」を明示します。

from pydantic import BaseModel

class Task(BaseModel):
    id: int
    title: str
    completed: bool

このモデルは Laravel における FormRequestAPI Resource に相当します。


🔁 FastAPI × 型ヒントの実用パターン

① リクエストボディに使う

@app.post("/tasks")
async def create_task(task: Task):
    return task

→ JSON のリクエストを Task 型として自動で受け取ってバリデーション


② レスポンスモデルに使う

@app.get("/tasks", response_model=List[Task])
async def get_tasks():
    return [
        {"id": 1, "title": "買い物", "completed": False},
        {"id": 2, "title": "掃除", "completed": True}
    ]

→ 複数のタスクを返すとき、戻り値の構造を型ヒントで明示できます。


🔄 Laravel との比較表

Laravel FastAPI
FormRequest BaseModel(Pydantic)
API Resource response_model=...
@return JsonResponse 関数戻り値 + response_model 指定

❓ よくある質問

Q. response_modelList[Task] と書く意味は?

Task 型のリスト(配列)を返すことを FastAPI に伝えます。

Q. Pythonの型ヒントって FastAPI でしか使わないの?

→ いいえ、FastAPI以外でも使えます。Pythonの標準機能です。

Q. 型ヒントは必須?

→ FastAPIでは型ヒントを使うと、ドキュメント生成や自動バリデーションが有効になります。ベストプラクティスです。


🔗 参考リンク

ぬぬぬぬ

📦 FastAPI × Pydantic モデルの基本:Field の使い方と型定義

🎯 この記事の目的

FastAPI の API 開発でよく使われる Pydantic モデルにおける、
フィールドの定義・型ヒント・Field() の使い方を Laravel の知識と対比しながら解説します。


✅ サンプルコード

from pydantic import BaseModel, Field

class Task(BaseModel):
    id: int
    title: str | None = Field(None, example="クリーニングを取りに行く")
    done: bool = Field(False, description="完了フラグ")

このモデルは、タスク情報をJSONで送受信する構造を定義しています。


🔍 フィールド解説

フィールド 型定義 意味
id int タスクID(必須)
title str | None タイトル(省略可、null許容、例付き)
done bool = Field(False, …) 完了フラグ(省略時はFalse、自動ドキュメントに説明が表示される)

🛠 Field() の役割

Pydantic の Field() 関数は、各フィールドに以下を追加できます:

パラメータ 説明
default デフォルト値の指定
example OpenAPIドキュメント上の例示
description OpenAPIドキュメント上の説明文
title, ge 他にも制約や説明用途が豊富

🔁 Laravelとの対比

Laravelの機能 FastAPI / Pydantic の機能
FormRequest BaseModel(型とバリデーションを定義)
nullable Optional[str] または `str None`
default, withMeta() Field(default=..., description=...)
API Resource response_model=Task で自動整形

❓ よくある疑問

Q1. Field() を使わずに title: Optional[str] = None だけでも良い?

→ はい、動作はします。ただし exampledescription をドキュメントに表示したい場合は Field() を使うのがベストです。

Q2. str | NoneOptional[str] の違いは?

→ 同じ意味です。str | None は Python 3.10 以降の簡潔な書き方です。

Q3. 省略されたフィールドはどうなる?

→ デフォルトが設定されていればその値が使われ、なければバリデーションエラーになります。


📘 参考リンク

ぬぬぬぬ

🧠【Python/Pydantic入門】入れ子クラス class Config の正体とその使い方

FastAPIやPydanticを学んでいると、次のような「クラスの中にクラスがある」書き方に出会うことがあります:

class Task(BaseModel):
    title: str

    class Config:
        orm_mode = True

Laravelなどの経験がある方にとって、この構文は少々不思議に見えるかもしれません。
今回は、この class Config の意味と使い方についてわかりやすく解説します!


🔸 入れ子クラスとは?

Pythonでは、クラスの中にクラスを定義することができます。これは「ネストされたクラス(入れ子クラス)」と呼ばれ、次のように使います:

class Outer:
    class Inner:
        def greet(self) -> str:
            return "こんにちは、Innerです"

    def create_inner(self) -> "Outer.Inner":
        return self.Inner()

    def say_hello(self) -> str:
        inner = self.create_inner()
        return inner.greet()

このように定義した場合、Outer.Inner() として直接インスタンス化もできますが、通常は Outer クラスの中で使用し、論理的なまとまりを表現するために使います。

通常の呼び出し方

outer = Outer()
print(outer.say_hello())  # → こんにちは、Innerです

🔸 class Config の役割

Pydanticでは、モデルの設定を指定するために特別なクラス Config を定義します。
これはモデルごとに適用される設定で、次のような項目を指定できます:

class Task(BaseModel):
    title: str

    class Config:
        orm_mode = True
        title = "タスクモデル"
        anystr_strip_whitespace = True

これにより、Pydanticは次のようなことが可能になります:

  • ORM(例:SQLAlchemy)のオブジェクトから直接データを読み取る(orm_mode = True
  • JSON Schema 生成時のタイトルを設定する(title
  • 文字列フィールドの前後の空白を自動で削除する(anystr_strip_whitespace

Pydanticは BaseModel の中で自動的に Config クラスを探して、それを内部的に参照します。
これは Laravel の $fillable$casts にあたる設定に似ています。


🔸 Laravelとの対比

Laravelの書き方 Pydanticの書き方
$fillable = ['name', 'email']; class Config: orm_mode = True
$table = 'users'; class Config: title = "ユーザー"

どちらも「モデルの挙動を定義する設定」を明示的にクラス内で行っている点が共通しています。


🔍 よくある疑問と回答

Q1. Config を外に出して定義したらダメ?

はい、Config は必ずモデルの中に入れ子で定義してください。Pydanticはその位置の Config クラスしか認識しません。

Q2. Outer.Inner() として直接使ってもいい?

技術的にはOKですが、設計上の意図(外部から直接使うべきでない)を明確にするために、外側のクラス経由で使うのが一般的です。

Q3. Config に他にどんな設定がある?

  • json_encoders
  • validate_assignment
  • allow_mutation
  • extra = "forbid" など多数あります。

👉 公式一覧はこちら: Pydantic Model Config


📝 まとめ

  • Pythonではクラスの中にクラス(ネストクラス)を定義できます。
  • class Config は Pydanticモデルの動作を調整するための特別なクラス。
  • orm_mode = True を使うとORMモデル(SQLAlchemyなど)と連携が可能に。
  • Laravelの $fillable$casts に近い概念で、設定のカプセル化ができます。
ぬぬぬぬ

FastAPI × Dev Containers で MySQL を導入する手順まとめ

はじめに

FastAPI プロジェクトで MySQL を使いたい場合、VS Code の Dev Containers 機能を活用すると、Laravel sail のように「アプリ+DB」を一括で管理できます。本記事では、実務経験者向けに「どのファイルをどう編集すればよいか」「Laravel との違い」も交えて、最短ルートで解説します。


手順(STEP)

  1. docker-compose.yaml に MySQL サービスを追加
  2. .devcontainer/devcontainer.json で dockerComposeFile と service を指定
  3. (必要に応じて).env でDB接続情報を管理
  4. Python 側で MySQL ドライバや ORM を導入
  5. VS Code で Dev Container を再構築(Rebuild Container)

サンプル構成

1. docker-compose.yaml

version: '3.8'
services:
  app:
    build:
      context: .
      dockerfile: .devcontainer/Dockerfile
    volumes:
      - .:/workspace
    ports:
      - "8000:8000"
    depends_on:
      - db
    environment:
      - MYSQL_HOST=db
      - MYSQL_USER=app_user
      - MYSQL_PASSWORD=app_pass
      - MYSQL_DATABASE=app_db

  db:
    image: mysql:8.0
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: rootpassword
      MYSQL_DATABASE: app_db
      MYSQL_USER: app_user
      MYSQL_PASSWORD: app_pass
    ports:
      - "3306:3306"
    volumes:
      - db_data:/var/lib/mysql

volumes:
  db_data:

2. .devcontainer/devcontainer.json

{
  "name": "fastapi-dev",
  "dockerComposeFile": [
    "../docker-compose.yaml"
  ],
  "service": "app",
  "workspaceFolder": "/workspace",
  "settings": {
    "terminal.integrated.defaultProfile.linux": "bash"
  },
  "postCreateCommand": "poetry config virtualenvs.in-project true && poetry install --no-root",
  "postStartCommand": "poetry run uvicorn api.main:app --host 0.0.0.0 --reload",
  "customizations": {
    "vscode": {
      "extensions": ["ms-python.python", "ms-azuretools.vscode-docker"]
    }
  },
  "forwardPorts": [8000]
}

3. .env(例)

MYSQL_HOST=db
MYSQL_PORT=3306
MYSQL_DATABASE=app_db
MYSQL_USER=app_user
MYSQL_PASSWORD=app_pass

ポイント解説

  • dockerComposeFile
    devcontainer.jsondockerComposeFile を指定することで、複数サービス(FastAPI+MySQL)を一括管理できます。
    Laravel sail も同じく docker-compose を使っています。

  • service
    どのサービス(コンテナ)を「開発用」として VS Code で attach するかを指定します(例: "service": "app")。

  • DB接続情報
    Laravel の .env と同じく、FastAPI 側も .env で管理し、os.getenv() などで読み込みます。

  • 反映方法
    設定変更後は「Dev Containers: Rebuild Container」を実行しましょう。


よくある質問

Q1. devcontainer.json で dockerComposeFile を書かないとどうなる?
A. 単一の Dockerfile だけで構築され、MySQL など他サービスは使えません。

Q2. 既存の Dockerfile も使いたい場合は?
A. compose の buildcontextdockerfile を指定してください。

Q3. 変更後に反映されない場合は?
A. 「Dev Containers: Rebuild Container」を実行してください。


参考リンク


おわりに

Laravel での sail と同じ感覚で、FastAPI プロジェクトにも MySQL を簡単に追加できます。
「どの compose ファイルを編集するか」「devcontainer.json での指定」を意識すれば、チーム開発もスムーズです。

ぬぬぬぬ

🐍 FastAPI + SQLAlchemy 入門:get_db() の意味を最初からわかりやすく解説!

FastAPI + SQLAlchemy のチュートリアルでよく見かけるこのコード:

def get_db():
    with db_session() as session:
        yield session

一見シンプルですが、初心者には「なぜこう書くの?」「この記法なに?」と疑問だらけですよね。
この記事では、この構文の裏側にある仕組みと意味を、Laravel との対比も交えてやさしく解説していきます。


📦 この記事でわかること

  • sessionmaker() って何?
  • with 文はなぜ使われるの?
  • yield の意味とは?
  • FastAPI が get_db() をどう使っているのか
  • Laravel での類似構造は?

🔧 コード全体の定番構成

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, declarative_base

DB_URL = "mysql+pymysql://root@db:3306/demo?charset=utf8"
db_engine = create_engine(DB_URL, echo=True)
db_session = sessionmaker(autocommit=False, autoflush=False, bind=db_engine)
Base = declarative_base()

def get_db():
    with db_session() as session:
        yield session

🧩 各構成要素の意味

1. create_engine()

  • DB接続を管理する「エンジン」を作成
  • Laravel でいう .env + DB::connection() に相当

2. sessionmaker()

  • セッション(DBとのやり取り用のオブジェクト)を作る工場
  • Laravel の DB::beginTransaction() 的な役割

3. with db_session() as session

  • 作られた Session オブジェクトは コンテキストマネージャ になっており、
  • with により、トランザクション完了後に自動で commit() / rollback() / close() を実行

Laravel の DB::transaction() に似た安全構文です。

4. yield session

  • FastAPI の依存性注入 (Depends(get_db)) と連携するために使用
  • 「このセッションを渡しますね」と言って一時停止(終了ではない!)
  • 終了後、with ブロックが閉じて session.close() が自動実行される

🎓 なぜこの構文で書くのか?

  • FastAPI は「リクエスト単位」でセッションを使いたい
  • セッションを使い終えたら確実にクローズしたい
  • with + yield によって、安全で省略のない実装ができる
  • よくあるトラブル(セッションが残る・DBロックされる)を防げる

🐘 Laravel との違い

処理内容 Laravel FastAPI
DB接続 .env による自動 create_engine() で明示的設定
トランザクション DB::transaction() with db_session()
モデルの継承 Model クラス Base = declarative_base()
DI(依存注入) サービスコンテナ Depends(get_db)
自動終了処理 フレームワークが吸収 明示的に with + yield

🤔 よくある疑問

Q. なぜ yieldreturn じゃダメ?

A. yield にすることで、FastAPI がリクエスト終了後に with ブロックを正しく閉じてくれます。return だと後始末されません。


Q. with 文は何にでも使えるの?

A. __enter__()__exit__() を実装している コンテキストマネージャ に対してのみ使えます。SQLAlchemy の Session はそれに対応しています。


Q. get_db() は定型文で覚えてよい?

A. 最初はOK! ただし「なぜこう書くのか」をうっすらでも理解しておくと、将来トラブル時にとても強くなれます。


🧠 おわりに:定型文 + 少しの理解が最強!

FastAPI + SQLAlchemy は Laravel よりも明示的に書くスタイルが求められますが、
最初はこの get_db() パターンを 定型文として使いつつ、意味を少しずつ理解していけばOK です。

「なぜこうするの?」と思ったら、今回のように少しずつ分解して学んでいきましょう。
きっと LangChain や LLM 開発のときに、この理解があなたを助けてくれます✨


🔗 参考リンク

ぬぬぬぬ

Laravel と FastAPI(SQLAlchemy)でのモデル設計比較:Task / Done モデル編

🎯 この記事の目的

Laravel の Eloquent モデルに慣れている方が、FastAPI + SQLAlchemy のモデル構造をスムーズに理解するために、両者のコードを構造比較します。

今回は「タスク(Task)」と「完了状態(Done)」を表すモデルを題材に、1対1リレーション(hasOne) を中心に解説します。


📌 モデル構造の概要

Laravel での構成

  • TaskDone1つ 所持(hasOne
  • DoneTask に属する(belongsTo
// app/Models/Task.php
class Task extends Model
{
    protected $table = 'tasks';
    protected $fillable = ['title'];

    public function done(): HasOne
    {
        return $this->hasOne(Done::class, 'task_id');
    }
}
// app/Models/Done.php
class Done extends Model
{
    protected $table = 'dones';

    public function task(): BelongsTo
    {
        return $this->belongsTo(Task::class, 'task_id');
    }
}

FastAPI + SQLAlchemy での構成

# api/models/task.py
class Task(Base):
    __tablename__ = "tasks"

    id = Column(Integer, primary_key=True)
    title = Column(String(1024))
    done = relationship("Done", back_populates="task", cascade="delete")
# api/models/done.py
class Done(Base):
    __tablename__ = "dones"

    id = Column(Integer, ForeignKey("tasks.id"), primary_key=True)
    task = relationship("Task", back_populates="done")

🔁 コード構造の比較表

概念 Laravel FastAPI + SQLAlchemy
モデル定義 extends Model class Task(Base)
テーブル名指定 $table __tablename__
カラム定義 $fillable Column()
リレーション定義 hasOne() / belongsTo() relationship() + ForeignKey()
自動ロード with() joinedload()(オプション)

💬 よくある疑問

Q1. hasOne() とは何ですか?

hasOne() は「このモデルは、1つの〇〇を持つ」という1対1リレーションの定義です。
Done 側に外部キー(例:task_id)を持つ必要があります。

Q2. Python でのリレーションはなぜ relationship() を使うの?

→ Python ではリレーションも属性として扱えるようにするため、オブジェクト指向的に relationship() を使います。

Q3. モデルファイルは分けるべき?

→ はい。Laravel と同様、モデルごとにファイル分割することで保守性が高まり、構造も見通しやすくなります。


🧠 学習のヒント

  • Laravel における hasOne()belongsTo() の考え方は、SQLAlchemy の relationship()ForeignKey() でそのまま表現可能。
  • モデルにロジックを持たせるか、サービス層に分離するかの方針はプロジェクト設計次第。
  • 入力制御(バリデーション)は Laravel では $fillableFormRequest、FastAPI では Pydantic モデルで行う。

🔗 参考リンク

ぬぬぬぬ

🎓 FastAPI における「依存注入(DI)」とは何か?Laravel 実務者向けまとめ

はじめに

FastAPI でよく出てくる Depends()Session の注入。
Laravel 実務経験のある開発者からすると、「DI(依存注入)」という設計パターンはどこかフワッとした印象を受けるかもしれません。

本記事では、DI の意味・目的・メリットを実例と比喩を交えて解説し、FastAPI 初学者でも腑に落ちるようにまとめました。


🤖 依存注入(Dependency Injection)とは?

依存注入とは、必要なもの(依存)を自分で作らず、外から渡してもらう設計です。

  • ❌ 悪い例:関数やクラスが内部で new して依存を作る
  • ✅ 良い例:関数やクラスが「使うだけ」でよく、作り方は外部に任せる

Laravel の例

public function store(TaskRequest $request, TaskService $service)

Laravel のサービスコンテナが $service を自動注入。これが「依存注入」です。


🔁 FastAPI における DI

FastAPI では Depends() を使って依存を注入します。

@app.post("/tasks/")
def create_task(task: TaskCreate, db: Session = Depends(get_db)):
  • task: TaskCreate は Pydantic による 入力バリデーション済みのリクエスト
  • db: Session = Depends(get_db) が DI の本質部分
    get_db() の戻り値を自動で注入してくれます。

☕ 依存注入のたとえ話

❌ 注入しない場合

コーヒーを作るのに、豆を自分で市場に買いに行く

✅ 注入する場合

冷蔵庫に材料が用意されていて、料理人は調理に集中できる

依存注入とは、「材料を渡してもらう設計」です。


🧪 ハンズオン:手動 vs DI の比較

🔧 共通前提

  • SQLite を使った SQLAlchemy モデル Task
  • Pydantic でバリデーション TaskCreate

A. 手動で Session を作る

@app.post("/manual")
def create_task_manual(task: TaskCreate):
    db = SessionLocal()  # 手動でセッションを作る
    ...
    db.close()

B. DI(依存注入)で Session を渡す

def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()

@app.post("/di")
def create_task_di(task: TaskCreate, db: Session = Depends(get_db)):
    ...

✅ 比較結果

観点 手動 DI(Depends)
セッションの生成 自前で管理 フレームワークに任せる
テスト モック不可 モック容易
責任の分離 曖昧 明確
保守性 低い 高い

🧠 DI が生まれた背景

依存を内部で作ってしまうと:

  • 差し替えができない(密結合)
  • テストがしにくい
  • 保守性が下がる

→ 外から渡すことで、再利用性・拡張性・テスト性が向上します。


📘 まとめ

FastAPI の Depends() は Laravel のサービスコンテナのように、依存を明示的に注入してくれます。
それにより、ルート関数やサービスロジックは自分の役割だけに集中できる、保守性の高い設計が可能になります。

「なんでわざわざ Depends にするの?」と思ったら、**“注入する責任の分離”**を思い出してください。


🔗 参考リンク

ぬぬぬぬ

✅ なぜ「依存注入」と呼ぶのか?

🎯 要点だけまず押さえましょう

  • 依存(Dependency):その関数・クラスが「動くために必要な部品」

    • 例:データベース接続、外部API、設定情報、認証ユーザーなど
  • 注入(Injection):その依存を「引数で外から渡すこと」

❌ 依存を自分で生成(=注入してない)

def create_user():
    db = SessionLocal()  # ← 自分で依存を生成してる(密結合)

✅ 依存を外から受け取る(=注入)

def create_user(db: Session):
    ...

→ この「db」が外から注入されている(Injected)依存だから、**依存注入(Dependency Injection)**と呼ばれます。


🧠 名前に惑わされず、構造を押さえる!

設計パターン 意味
依存注入(DI) 必要なものを外から渡す(引数やプロパティなど)
依存解決(Dependency Resolution) その渡す“仕組み”をどう作るか(DIコンテナなど)
制御の反転(IoC) 「使う側が作る」から「使われる側に任せる」構造全体

💬 たとえ話で確認!

👩‍🍳 料理人(関数)が、材料(依存)を毎回スーパーで買いに行く?
→ いいえ、材料はキッチン(外部)から受け取る方が効率的です。

それがまさに **「依存注入」**の考え方です。


✅ FastAPI ではこれが自動でできる

@app.get("/users/")
def read_users(db: Session = Depends(get_db)):
  • 関数 read_users() に必要な db(依存)を
  • FastAPI が get_db() を通じて注入してくれる
  • 開発者は「どう作るか」を気にしなくてOK

✅ Laravel でも同じ思想

public function store(UserService $service)

Laravel のサービスコンテナが $service を自動で解決・注入する。

ぬぬぬぬ

🤖 FastAPI の「依存注入は自動」とはどういう意味か?

🧠 疑問の出発点

FastAPI のチュートリアルなどでよく見かけるこの文言:

「FastAPI は依存注入を自動でやってくれる」

でも、実際にはこう書いてるはず:

def create_user(db: Session = Depends(get_db)):

「明示的に引数に書いてるのに、どこが自動?」と疑問を持った方――大正解です✨


✅ 結論:「依存の生成と管理を自動化してくれる」という意味

✍ ユーザーが書くのは:

db: Session = Depends(get_db)
  • 「何が必要か」を宣言しているだけ
  • db をどうやって作るかまでは書いていない

⚙️ FastAPI が自動でやってくれること:

  1. 関数の引数を inspect で解析
  2. Depends(get_db) を検出
  3. get_db() を呼び出す(※ジェネレータ対応)
  4. 戻り値を db に代入
  5. 関数終了後に finally: db.close() を実行

🛠️ 手動との比較

❌ 手動で Session を扱うと…

def create_user():
    db = SessionLocal()
    try:
        ...
    finally:
        db.close()

→ 自分で Session の生成・破棄・例外管理をすべて書く必要があります。


✅ DI を使うと…

def create_user(db: Session = Depends(get_db)):
    ...

→ FastAPI がライフサイクル管理を丸ごと代行してくれる。


🎯 「自動」とは何が自動なのか?

要素 手動 FastAPI(DI)
依存の宣言 必要なし 明示的に書く
依存の生成・注入 自分で SessionLocal() Depends(get_db) を見て自動生成
終了処理(例: close) 自分で書く yield によって自動で行われる

🧬 Laravel と比較すると?

Laravel のこれ:

public function store(UserService $service)

$service を自分で new していないけど、勝手に渡ってくる。

FastAPI も同じで:

def create_user(service: UserService = Depends(get_service)):

→ 自動で get_service() が呼ばれて結果が service に注入される。


💡 まとめ

  • FastAPI における「依存注入の自動化」とは、

    • 必要なものの宣言は開発者が行い
    • その生成・管理・注入をフレームワークが行う
      という責任分離の設計です。
  • これによりコードはシンプルに、

    • テストもやりやすく、
    • 保守性も向上します。

📝 参考リンク

ぬぬぬぬ

🌱 マークアップコーダーのための「依存注入(DI)」入門

〜アニメーション実装にも活かせる設計思考〜

🎯 この記事の目的

普段 HTML / CSS / JavaScript で静的ページを作っている方へ――
「依存注入(DI)」という設計思想を聞いたことがあるけど、実際に何なのかピンとこないことはありませんか?

この記事では、マークアップ実務に寄り添った視点で DI を説明し、アニメーションやスクリプトの実装にどう応用できるかをご紹介します。


🤔 依存注入(DI)とは?

✅ 一言で言えば:

「必要なもの(依存)を、自分で探さず外から渡してもらう」設計


🍱 たとえ話で理解しよう!

❌ DI なし:料理人が自分で市場へ

function animateBox() {
  const el = document.querySelector('.box');
  el.classList.add('fade-in');
}

→ この関数は「.box が存在すること」に依存していて、他の要素には使い回せません


✅ DI あり:材料(依存)を渡してもらう

function fadeIn(el) {
  el.classList.add('fade-in');
}

const target = document.querySelector('.box');
fadeIn(target); // ← 依存を外から渡す

fadeIn() 関数は 「何をアニメーションするか」だけを受け取り、実装はシンプル&再利用可能


📦 DI 的設計のメリット

項目 DI なし DI 的設計
汎用性 固定的 どの要素にも使える
テスト性 ×
可読性 DOM取得やロジックが混在 関数の責任が明確
スタイル変更 書き換えにくい 切り替えやすい

🛠 実務応用例:JS アニメーション設計

モジュール化して複数箇所に使えるアニメーション関数

// animation.js
export function fadeIn(el, duration = 300) {
  el.style.transition = `opacity ${duration}ms`;
  el.style.opacity = 0;
  requestAnimationFrame(() => {
    el.style.opacity = 1;
  });
}
// main.js
import { fadeIn } from './animation.js';

const items = document.querySelectorAll('.fade-target');
items.forEach(el => fadeIn(el));

DOM選択とアニメーションのロジックを分離して、責任を明確にしています。
これが JavaScript における「依存注入的な設計」です!


🤖 フレームワークに進むと…?

React や Vue では、DI 的な設計はさらに自然になります。

// React: props(依存)を受け取って使う
function FadeInBox({ children }) {
  return <div className="fade-in">{children}</div>;
}

→ この考え方は、LPコーダーが将来的にアプリやUI開発に進むときに必ず役立ちます!


💡 まとめ

  • 「依存注入」とは、関数やコンポーネントが必要なものを外からもらう設計です
  • アニメーション処理などを「どの要素にも使える形」にすることで、保守性・再利用性・読みやすさが向上します
  • 難しく聞こえますが、「querySelector を中で書かない」くらいの発想から始めてOKです!

📘 関連資料

ぬぬぬぬ

Python AsyncIO完全ガイド:get_running_loop() / get_event_loop() / new_event_loop() の違いと使い方

Python の非同期処理を支える asyncio の核となるのが「イベントループ」です。本記事では、イベントループの役割と、以下3つの関数の違いと使い方について体系的に解説します。

  • get_running_loop()
  • get_event_loop()(非推奨)
  • new_event_loop()

Laravel や JavaScript の経験がある方にもわかりやすいように、対比を交えてお届けします。


🎯 イベントループとは?

イベントループは、非同期処理における「スケジューラ兼実行監視係」です。

  • 非同期タスクの順序管理
  • I/Oやタイマーなどのイベントを監視
  • 発生したイベントに応じて対応処理を再開

🏃‍♀️ たとえ話

「駅構内の放送係」=各ホームの状態を監視し、電車が来たら案内する


🔍 各関数の役割と違い

asyncio.get_running_loop()

現在実行中のイベントループを取得する。
非同期関数(async def)の内部でのみ使用可能。

async def main():
    loop = asyncio.get_running_loop()
    print(loop.time())

asyncio.run(main())

asyncio.new_event_loop()

新しいイベントループオブジェクトを明示的に作成する関数
同期関数内やスレッドで独自にループを作る際に使用される。

import asyncio

def setup_loop():
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)
    loop.run_until_complete(async_task())

asyncio.run() の内部でもこの関数が使われています。

asyncio.get_event_loop()(非推奨)

以前は「現在のイベントループを取得、なければ作成」する関数として使われていました。
しかし動作が曖昧でトラブルの原因になりやすく、Python 3.10以降では非推奨です

# 非推奨:将来的にRuntimeErrorになる可能性あり
loop = asyncio.get_event_loop()

🆚 使い分け早見表

関数名 使う場所 用途・目的 推奨度
get_running_loop() async def 実行中のループを取得(最安全) ✅ 高
new_event_loop() 同期関数やスレッド 新しいループの作成 ✅ 高
get_event_loop() どこでも(曖昧) 実行中 or 新規作成(非推奨) ❌ 低

🧠 Laravel / JavaScriptとの対比

概念 Laravel JavaScript / Node.js Python / asyncio
コンテキスト取得 request() this, currentTarget get_running_loop()
非同期の基盤 Queue / Job Event loop Event loop
明示的なループ作成 非常にまれ 自作しない new_event_loop()

🧪 使い分け例:簡単な比較コード

import asyncio

async def sample():
    print("ループ:", asyncio.get_running_loop())

def sync_context():
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)
    loop.run_until_complete(sample())

sync_context()

🙋‍♀️ よくある質問

Q1: いつ new_event_loop() を使うの?

  • スレッドごとにイベントループを分けたいとき
  • asyncio.run() を使わずに手動でループを制御したいとき

Q2: get_event_loop() が非推奨の理由は?

  • 同期関数内と非同期関数内で挙動が異なるため、予期せぬバグを生む
  • Python 3.12以降では RuntimeError を出す仕様に変更されつつある

✅ まとめ

  • get_running_loop():現在のイベントループを安全に取得(基本の選択肢
  • new_event_loop():手動でイベントループを作るときに使用
  • get_event_loop()使わないことを推奨

イベントループを正しく理解すると、FastAPI や LangChain、さらには非同期クローラーやチャットボット開発の基盤がしっかりします。非同期処理の世界に、勇気を持って踏み込んでいきましょう💪✨


📚 参考リンク

ぬぬぬぬ

Python asyncio入門:sleepの違い・並行処理・Taskの扱い・タイムアウトまで

はじめに

この記事では Python の非同期処理ライブラリ asyncio を使った非同期の書き方やその背後にある仕組みについて、順を追って深掘りしていきます。

特に以下の4つのトピックに重点を置いて解説します:

  1. asyncio.sleep() vs time.sleep() の違い
  2. 並行処理の実現方法(create_task() / gather()
  3. asyncio.Task オブジェクトと .result() の意味
  4. asyncio.wait_for() によるタイムアウト処理

1. asyncio.sleep() vs time.sleep():本当に非同期ですか?

await asyncio.sleep(sec) の特長

  • 非同期の「スリープ」であり、他の処理に制御を譲れる
  • CPU をブロックしない
  • FastAPI など非同期アプリケーションと相性が良い

time.sleep(sec)async def に使うのはNG

  • イベントループごとブロックしてしまう
  • 他の非同期タスクが止まる
  • 使うと FastAPI の並列処理性能が激減!

2. 並行処理の正しい方法:create_task() + await

✅ 並行実行のコード例

task1 = asyncio.create_task(func(1))
task2 = asyncio.create_task(func(2))
await task1
await task2

このように書くと、2つの関数が並行に開始されるため、実行時間は最長の処理時間に一致します。

❌ 間違いやすい例

await asyncio.create_task(func(1))
await asyncio.create_task(func(2))

この場合、task1 が終わってから task2 が始まるため、**順次実行(直列)**になります。


3. asyncio.Task オブジェクトと .result() の意味

create_task() の戻り値は Task オブジェクト

このオブジェクトは「非同期処理の状態と結果」を保持します。

.result() の使い方

task = asyncio.create_task(func())
await task
print(task.result())  # 完了後の戻り値を取り出す
  • await 後に使うこと(未完了で呼ぶと例外が出る)
  • .done().exception() で状態確認も可能

💡 Laravel でいうと dispatch() でキュー投入したJobの完了後の戻り値を取得するイメージ


4. タイムアウト処理:asyncio.wait_for()

✅ 書き方と効果

try:
    result = await asyncio.wait_for(func(10), timeout=3)
except asyncio.TimeoutError:
    print("タイムアウト")
  • 最大3秒まで待つ
  • 間に合わなければ TimeoutError
  • コルーチンは自動的にキャンセルされる

✅ よくある用途

  • 外部API呼び出し
  • ユーザー操作の待機
  • 長時間処理の保険

💡 Laravel でいう Http::timeout() と同じような使い方


おわりに

このスレッドでは asyncio の基本から「非同期的にどう書けばいいのか?」「なぜ非同期が効率的なのか?」まで、丁寧に整理してきました。

今後のおすすめテーマ

  • asyncio.gather()return_exceptions=True
  • タスクのキャンセル後処理(CancelledError
  • FastAPI における非同期ルーティング設計

参考リンク

ぬぬぬぬ

Python 非同期処理まとめ:コルーチンオブジェクト・await・create_task・gatherの使い分け


✅ サンプルコード全体

import asyncio

async def hello():
    print("hello 実行中")
    return "こんにちは"

async def main():
    # --- コルーチンオブジェクト ---
    coro = hello()  # 実行されない、ただの「処理のレシピ」
    print(f"type: {type(coro)}")  # <class 'coroutine'>

    # --- await ---
    result = await coro  # ここで初めて hello() の中身が実行される
    print("await の結果:", result)

    # --- create_task ---
    task = asyncio.create_task(hello())  # 即スケジューリング(バックグラウンドで実行開始)
    await task  # 実行完了まで待つ
    print("create_task の結果:", task.result())

    # --- gather ---
    results = await asyncio.gather(
        hello(), hello(), hello()
    )  # 並行に3つの hello() を実行し、すべての完了を待つ
    print("gather の結果:", results)

asyncio.run(main())

✅ 用語と構造のまとめ

概念 説明
コルーチンオブジェクト async def を呼び出した直後に返ってくる「まだ実行されていない非同期関数の中身」
await コルーチンオブジェクトを「今すぐ実行して結果を待つ」命令
create_task() コルーチンオブジェクトを「バックグラウンドで並行に実行するタスク」として登録
gather() 複数のコルーチンやタスクを「一括で並行実行」して「すべての結果を待つ」関数

✅ 使い分けの視点

目的 使うべき構文 説明
結果がすぐ欲しい await coro 順次処理に向いている
タスクを管理したい create_task() キャンセル、例外、進捗監視が必要なときに便利
複数同時に実行したい gather() 一括で並行実行し、すべての完了を待つのに最適
両方必要 gather(create_task(...)) 実行制御と並行実行の両立ができる

✅ 出力結果(例)

type: <class 'coroutine'>
hello 実行中
await の結果: こんにちは
hello 実行中
create_task の結果: こんにちは
hello 実行中
hello 実行中
hello 実行中
gather の結果: ['こんにちは', 'こんにちは', 'こんにちは']

✅ 結論

  • コルーチンオブジェクトは「未実行の非同期処理」
  • await は「実行して待つ」
  • create_task() は「並行に実行する」
  • gather() は「複数を一括実行して待つ」
ぬぬぬぬ

🧠 非同期 × 並行処理 の構造理解:FastAPIとJavaScriptから学ぶ本質

🎯 本記事の目的

「FastAPIを学んでいるけど、非同期処理や並行処理ってどういう仕組み?」「JavaScriptのasync/awaitとの違いは?」

そんな疑問を持つ方に向けて、本記事では以下を構造的に解説します:

  • 非同期処理と並行処理の違い
  • PythonとJavaScriptの設計思想の比較
  • FastAPIにおける並行処理の現実

🧩 1. 非同期と並行処理の違いとは?

用語 意味
非同期 「終わるのを待たずに先に進むこと」→ async/await, Promise
並行処理 「複数の処理を同時に“見せる”こと」→ イベントループやタスク切り替え
並列処理 「実際に複数が同時に動く」→ マルチスレッド、マルチプロセス

→ ✅ 非同期処理を使う目的は、I/O待ちの時間を利用して「擬似的な並行処理」をするためです。


🧪 2. PythonとJavaScriptの違い

観点 Python (asyncio) JavaScript (Promise)
明示的な並行化 create_task() + gather() Promiseだけで暗黙に並行
非同期関数の実行 await coro() const p = fetch()(即実行)
並行処理のスタート 明示的にスケジューリング 呼び出し時に即開始

→ JavaScriptでは非同期関数を呼んだだけで並行処理になるが、Pythonでは明示的な登録が必要です。


⚙️ 3. FastAPIはなぜ非同期設計なのか?

FastAPIは「ASGI + asyncio + async def」という3要素で構成され、HTTPリクエストごとにコルーチンが並行実行されます。

  • 並行処理のベースに非同期処理を採用
  • Depends(get_db) などで非同期セッションを生成
  • async def 関数内で await すればブロックせずにI/O待ちが可能

→ LaravelやFlaskの同期的設計と大きく異なり、「アプリケーション全体が非同期構造」である点が特徴です。


🧠 4. 非同期処理の書き分け(比較)

JavaScript

const a = fetch('/a');
const b = fetch('/b');
const [resA, resB] = await Promise.all([a, b]);

Python

task1 = asyncio.create_task(fetch_a())
task2 = asyncio.create_task(fetch_b())
resA, resB = await asyncio.gather(task1, task2)

→ JavaScriptでは fetch() 呼び出しだけで並行、Pythonでは create_task() or gather() が必要


🚀 5. 並行処理をしたいから非同期を使う ― 設計の視点

  • 並行処理をしたい(I/Oが重い、待ちたくない)
  • → スレッドはコストが高い
  • → async/await で「切り替え式の並行処理」

→ つまり 非同期処理は並行処理のための“手段”。この構造を理解して技術選定を行えるのが、設計者の視点です。


🧭 参考リンク

ぬぬぬぬ

Python 非同期処理の真実:「await の連鎖」と「async 伝播地獄」を正しく理解する

Python の非同期処理(async/await)を学び始めると、誰もが一度はぶつかる謎。

「非同期関数を呼ぶには await が必要 → その関数も async にしないといけない → 無限に async が伝播するのでは…?」

はい、これはいわゆる**「awaitの連鎖」**という現象であり、「永遠に async を付け続ける地獄」とも呼ばれたりします。


🧠 await は「中断・再開ポイント」

まず await の本質を整理しましょう。

🔸 await とは?

await は、処理を一時的に中断して、後で再開するためのチェックポイントです。
主に次のような目的で使われます:

  • API通信やDBアクセスなど「待ち時間の発生する処理」で他の処理をブロックしない
  • CPU時間を他のタスクに譲る

🔸 だからこそ、async が必要

await を使うには、その関数自体が「中断・再開可能」である必要があります。
つまり Python にこう宣言するのです:

「この関数は await を使うので、イベントループから中断・再開される可能性がありますよ」

これが async の正体です。
したがって await を含むには、その関数自体に async を付ける必要があるのです。


🔁 そして始まる…await の連鎖

こうして関数Aが await を使うために async にしたら、
それを呼ぶ関数Bも await しないといけなくなり、
結果として関数Bも async に…。

これが「await の連鎖」= async の伝播地獄です。

async def func_a():
    await something()

async def func_b():
    await func_a()

async def func_c():
    await func_b()

このように 非同期関数は「上流に async が伝播」していく構造になります。


🧯 でもご安心を:「asyncio.run()」がある

「じゃあこの連鎖、どこで止まるの?」という問いに対する答えがこちら:

async def main():
    await func_c()

asyncio.run(main())  # 🔑 非同期世界のエントリーポイント

この asyncio.run() が、非同期文脈の始点かつ終点です。
つまり:

非同期は「asyncio.run() から始まり、その中で async/await が再帰的に展開される」構造なのです。


🆚 JavaScriptとの違いは?

比較項目 Python JavaScript
非同期関数 async def async function
非同期呼び出し await func() await func()
イベントループ asyncio.run() で明示起動 ランタイムが暗黙的に常時動作
awaitの制限 async の中でしか使えない 同様(ただしESMではトップレベル await 可能)

→ JavaScript はイベントループが自動で回っているため、Pythonより柔軟に見えます。
ただし設計的には Pythonの方が明示的で、バグが起きにくいという利点があります。


🧘 結論

  • await処理の中断・再開ポイントを示す記号
  • asyncその中断再開を許可する文脈マーカー
  • だから await がある関数には async が必要
  • 結果として「await の連鎖」が発生し、async が伝播する構造になる
  • それを支えるのが asyncio.run() という「非同期世界の起点」

この設計は慣れるまで「冗長」に感じるかもしれません。
でも、それは 非同期を安全かつ予測可能に扱うための強力な設計ルールです。

ぬぬぬぬ

Pythonの awaitcreate_task() の違いを理解する【JavaScript経験者向けまとめ】

Pythonで非同期処理を行う際に登場する awaitasyncio.create_task()
JavaScript における awaitPromise に慣れている方にとっては、似ているようで微妙な違いが気になるところ。

本記事では、「タスクは開始されるのか?」return がないなら await する意味あるの?」といった疑問に答えながら、
Pythonの非同期処理の使い方と設計意図を JavaScript・Laravel と比較しつつ整理します。


✅ 1. await asyncio.create_task(func())task = asyncio.create_task(func()); await task の違い

書き方 意図 特徴
await asyncio.create_task(func()) 作ってすぐ待つ func() は即実行され、完了までブロックされる
task = asyncio.create_task(func())
await task
タスクの制御を分離 タスク開始と待機を分けて管理できる(柔軟)

✅ 共通点

  • どちらも 関数 func() の実行はすぐ始まる
  • await しないと「完了まで待たない」ので注意

✅ 2. create_task() したら実行されるの?

はい、されます!

task = asyncio.create_task(hello())  # hello() の実行はこの時点で始まる!

JavaScriptで言うと:

const promise = fetchData(); // ここでもう fetchData() は走ってる

✅ 3. await の意味って戻り値のため?

いいえ、それだけではありません。
await には 3つの目的 があります:

目的 説明 return が必要?
✅ 完了を待つ 後続処理との整合を保つ
✅ 戻り値を使う 処理結果を取得
✅ エラーを拾う 非同期タスクの例外ハンドリング

✅ 4. 「完了を待ち、戻り値を使う」典型パターン

非同期処理の結果をもとに次の処理をしたい場面では、await が不可欠です。

async def fetch_user_data(user_id: int) -> dict:
    await asyncio.sleep(1)  # 模擬的なAPI呼び出し
    return {"id": user_id, "name": "Alice"}

async def main():
    print("データ取得中…")
    user = await fetch_user_data(42)  # ✅ 完了を待って、戻り値を使う
    print("取得結果:", user)

asyncio.run(main())

このように await によって:

  • 処理の 完了を保証しつつ
  • 戻ってきたデータを次の処理に活かせる という流れができます。

まさに JavaScript の以下と同じ考え方です:

const user = await fetchUserData(42);
console.log("取得結果:", user);

✅ 5. JavaScriptとの違いと共通点

比較項目 JavaScript Python
非同期定義 async function async def
処理の待機 await await
並列処理 Promise.all asyncio.gather()
タスク管理 Promise Task (create_task)

✅ 共通する考え方

  • await は「非同期関数の完了を待つ」もの
  • create_task() は「バックグラウンドで走らせつつ、後で制御したいとき」に使う

🎯 総まとめ

  • create_task() は「非同期関数をイベントループに登録して即時開始」する
  • await は「その関数の完了を待つ/戻り値を取得する/エラーを拾う」
  • return がなくても await処理の順序保証のために意味がある
  • JS の Promise / await とかなり近いが、Python は「タスク制御」に強い
  • 「完了を待ち、戻り値を使って次の処理をする」=await の基本ユースケース!

📚 参考リンク