FastAPIで独自のフォーラムを構築:ステップ1 - ミニマルフォーラム
Takashi Yamamoto
Infrastructure Engineer · Leapcell

市場にはすでに多くのフォーラム製品がありますが、どれもあなたのユニークなニーズを満たせず、まだイライラしていませんか?もしそうなら、なぜ自分でフォーラムをゼロから構築しないのでしょうか?
Discourseのような巨大なフォーラムSaaSプラットフォームに臆することはありません。フォーラムの構築は、思っているほど難しくありません。
今後の記事シリーズでは、人気のあるPython WebフレームワークであるFastAPIを使用して、完全に機能するモダンなフォーラムWebサイトをゼロから段階的にガイドします。
このチュートリアルシリーズは初心者向けです。この記事の最後までに、以下に示すような、実行可能なミニフォーラムが完成するでしょう。
早速始めましょう:
ステップ1:環境セットアップ
まず、開発環境を準備しましょう。
プロジェクトディレクトリと仮想環境の作成
プロジェクト専用のフォルダを作成し、その中に仮想環境を作成します。
簡単のため、Pythonの組み込みvenv
モジュールを使用します。poetry
のような、他にも素晴らしい仮想環境ツールがたくさんあります。これらについてはご自身で調べてみてください。
# プロジェクトディレクトリを作成して移動 mkdir fastapi-forum cd fastapi-forum # 仮想環境を作成 python3 -m venv venv # 仮想環境をアクティベート # Windows: # venv\Scripts\activate # macOS / Linux: source venv/bin/activate
アクティベーションが成功すると、コマンドラインプロンプトの先頭に(venv)
が表示されます。
FastAPIとUvicornのインストール
次に、この記事で必要となるコアライブラリ、fastapi
とuvicorn
をインストールします。
fastapi
: フレームワーク自体。uvicorn
: FastAPIアプリケーションを実行するために使用されるASGIサーバー。
pip install fastapi "uvicorn[standard]"
"uvicorn[standard]"
は、最適なパフォーマンスのために推奨される依存関係とともにuvicornをインストールします。
ステップ2:「こんにちは、フォーラム!」 FastAPIアプリを実行する
環境が整ったので、最初のFastAPIコードを記述しましょう。fastapi-forum
ディレクトリにmain.py
という名前のファイルを作成し、以下の内容を追加します:
main.py
from fastapi import FastAPI app = FastAPI() @app.get("/") def read_root(): return {"message": "Hello, Forum!"}
このコードは何をするのでしょうか?
FastAPI
のインスタンスを作成し、app
と名付けます。@app.get("/")
はデコレーターです。これは、以下のread_root
関数がパス/
に来るGET
リクエストを処理することを示しています。- ルートパスにリクエストが到達すると、関数はPython辞書を返します。FastAPIはこれを自動的にJSONレスポンスに変換します。
次に、ターミナルからアプリケーションを実行します:
uvicorn main:app --reload
main
:main.py
ファイルを指します。app
:main.py
で作成したFastAPI
インスタンスapp
を指します。--reload
: コード変更後にサーバーが自動的に再起動する、非常に便利なパラメータです。
これで、ブラウザを開いてhttp://127.0.0.1:8000
にアクセスすると、以下のように表示されます:
{ "message": "Hello, Forum!" }
おめでとうございます!最初のFastAPIアプリケーションが正常に実行されました!
ステップ3:コアデータ構造の定義
私たちのフォーラムは投稿を公開するためのものなので、「投稿」がどのようなものかを定義する明確なデータ構造が必要です。FastAPIでは、データ定義と検証にPydantic
を使用することを推奨しています。これはFastAPIに組み込まれており、非常に強力です。
main.py
にPost
モデルを定義しましょう。
main.py
(更新版)
from fastapi import FastAPI from pydantic import BaseModel from typing import List # 1. データモデルの定義 class Post(BaseModel): id: int title: str content: str app = FastAPI() # 2. インメモリデータベースとしてリストを使用 # 注:これはデモンストレーション目的のみです。サーバー再起動でデータは失われます! db: List[Post] = [ Post(id=1, title="What is FastAPI?", content="A modern, high-performance Python web framework..."), Post(id=2, title="Introduction to Pydantic", content="Pydantic is a library for data validation and settings management..."), ] @app.get("/") def read_root(): return {"message": "Welcome to my forum!"} # 後続のAPIはここに追加されます...
pydantic.BaseModel
を継承するPost
クラスを作成しました。投稿には3つのフィールド、id
(整数)、title
(文字列)、content
(文字列)が必要であると定義しました。
また、db
という名前のリストを作成し、データベースをシミュレートするために2つのPost
オブジェクトを事前に入力しました。
ステップ4:コアAPIの実装
基本的なフォーラムには、投稿の表示と投稿の作成という2つの機能が必要です。対応するAPIエンドポイントを実装しましょう。
1. 全投稿の取得
db
リスト内のすべての投稿を返すエンドポイントが必要です。main.py
に以下のコードを追加します:
main.py
(継続)
... # 前のコードは変更なし # 3. 投稿リストを取得するAPI @app.get("/api/posts", response_model=List[Post]) def get_posts(): return db
@app.get("/api/posts")
:GET
リクエストを処理する新しいパス/api/posts
を定義します。response_model=List[Post]
: このエンドポイントのレスポンスボディはPost
オブジェクトのリストになることをFastAPIに伝えます。FastAPIはこれをデータ検証、変換、およびAPIドキュメントでの明確なドキュメントに使用します。
2. 新規投稿の作成
次に、新しい投稿を作成するためのエンドポイントです。
まず、フォームデータの処理をサポートするためにpython-multipart
をインストールします:
pip install python-multipart
main.py
(継続)
# ... 前のコードは変更なし # 4. 新規投稿を作成するAPI @app.post("/api/posts", response_model=Post) def create_post(title: str = Form(...), content: str = Form(...)): new_id = len(db) + 1 new_post = Post( id=new_id, title=title, content=content ) db.append(new_post) return new_post
title: str = Form(...)
というパラメータ形式は、FastAPIにフォームフィールドからデータを抽出するように指示します。
入力/出力形式がフォームフィールドであるのはなぜですか?後でユーザーがフォームを通じて投稿コンテンツを送信できるHTMLページを作成するためです。
ステップ5:シンプルなインタラクティブページ
純粋なAPIはフォーラムとは呼べません。FastAPIのHTMLResponse
を使用して、ユーザーがブラウザで直接投稿を作成および表示できる、実行可能なHTMLページをすばやく作成できます。
このページを表示するために新しいパスGET /posts
を作成します。
main.py
(最終完全版)
from fastapi import FastAPI, Form from fastapi.responses import HTMLResponse, RedirectResponse from pydantic import BaseModel from typing import List # --- データモデル --- class Post(BaseModel): id: int title: str content: str # --- インメモリデータベース --- db: List[Post] = [ Post(id=1, title="What is FastAPI?", content="A modern, high-performance Python web framework..."), Post(id=2, title="Introduction to Pydantic", content="Pydantic is a library for data validation and settings management..."), ] app = FastAPI() # --- HTMLテンプレート --- def generate_html_response(): posts_html = "" for post in reversed(db): # 新しい投稿を上に表示 posts_html += f""" <div style="border: 1px solid #ccc; padding: 10px; margin-bottom: 10px;"> <h3>{post.title} (ID: {post.id})</h3> <p>{post.content}</p> </div> """ html_content = f""" <html> <head> <title>My FastAPI Forum</title> <style> body {{ font-family: sans-serif; margin: 2em; }} input, textarea {{ width: 100%; padding: 8px; margin-bottom: 10px; box-sizing: border-box; }} button {{ padding: 10px 15px; background-color: #007BFF; color: white; border: none; cursor: pointer; }} button:hover {{ background-color: #0056b3; }} </style> </head> <body> <h1>Welcome to My Forum</h1> <h2>Create a New Post</h2> <form action="/api/posts" method="post"> <input type="text" name="title" placeholder="Post Title" required><br> <textarea name="content" rows="4" placeholder="Post Content" required></textarea><br> <button type="submit">Post</button> </form> <hr> <h2>Post List</h2> {posts_html} </body> </html> """ return HTMLResponse(content=html_content, status_code=200) # --- ルート --- @app.get("/", response_class=RedirectResponse) def read_root(): # ルートパスをビューページにリダイレクト return "/posts" # ページを表示するルート @app.get("/posts", response_class=HTMLResponse) async def view_posts(): return generate_html_response() @app.post("/api/posts") def create_post(title: str = Form(...), content: str = Form(...)): new_id = len(db) + 1 new_post = Post( id=new_id, title=title, content=content ) db.append(new_post) # 更新効果のために、投稿後にメインページにリダイレクト return RedirectResponse(url="/posts", status_code=303)
generate_html_response
関数を使用して、フォームと投稿リストを含む完全なHTMLページを動的に生成します。これは原始的ですが、この段階では完全に十分です。
GET /posts
は生成されたHTMLページを直接返します。これにより、ユーザーが/posts
にアクセスするとフォーラムインターフェースが表示されます。さらに、ルートパス/
は/posts
にリダイレクトされるため、ルートURLにアクセスしたユーザーは自動的にフォーラムページに移動します。
GET /api/posts
が削除されていることに気づくかもしれません。これは、投稿リストを取得するために別のAPIを必要としなくなったためです。ページはインメモリdb
から直接データを読み取ります。
また、POST /api/posts
のロジックも変更しました。Post
オブジェクトを返すのではなく、/posts
ページにリダイレクトするようになり、ユーザーは送信後に更新された投稿リストをすぐに確認できます。
フォーラムの実行
Uvicornサーバーがまだ実行中であることを確認してください。http://127.0.0.1:8000
をリフレッシュまたは再訪問すると、既存の投稿と新しい投稿を作成するためのフォームが表示されたフォーラムページが表示されます。
フォームにコンテンツを入力して送信すると、新しい投稿が投稿リストに表示されます。
プロジェクトのオンラインデプロイ
フォーラムは皆に使ってもらうためのものなので、ローカルで実行するだけでは十分ではありません。次に、オンラインでデプロイしましょう。
簡単なデプロイオプションはLeapcellを使用することです。これは、FastAPIを含むさまざまな言語やフレームワークのプロジェクトをホストできるWebアプリケーションホスティングプラットフォームです。
以下の手順に従ってください:
- ウェブサイトでアカウントを登録します。
- プロジェクトをGitHubにコミットします。GitHubの公式ドキュメントを参照して手順を確認してください。Leapcellは後でGitHubリポジトリからコードを取得します。
- Leapcellページで「Create Service」をクリックします。
- FastAPIリポジトリを選択すると、Leapcellが必要な構成を自動入力したことが表示されます。
- 下部にある「Submit」をクリックしてデプロイします。デプロイはすぐに完了し、デプロイホームページに戻ります。ここで、Leapcellがドメインを提供していることがわかります。これがご自身のブログのオンラインアドレスです。
結論
わずかなステップで、FastAPIを使用して、最も基本的な機能を備えたフォーラムプロトタイプをゼロから構築しました。
このフォーラムはまだ非常に不完全です。例えば、サーバーが再起動すると、すべての新しい投稿が消えてしまいます。これは明らかに許容できません。
次の記事では、永続的なデータストレージを可能にするために、実際のデータベースをフォーラムに導入します。