はじめに
フロントエンドの準備が整ったら、次はバックエンドの構築です。
本記事では、PythonのFastAPIを用いてユーザー認証機能を実装する方法を解説します。
バックエンドAPIの基礎を学びながら、フロントエンドとの連携を見据えた設計を進めていきましょう。
環境構築|仮想環境とライブラリをインストール
FastAPI
フォルダ構成
TEST
---backend
------main.py
---frontend
---売上データ
------売上データ2020.csv
------売上データ2021.csv
------売上データ2022.csv
仮想環境作成
cd backend
python -m venv .venv
仮想環境に入る
.venv\Scripts\activate
ライブラリをインストール
pip install "fastapi[all]"
pip install pydantic
pip install sqlmodel
pip install passlib[bcrypt]
FastAPI起動
uvicorn main:app --reload
APIのドキュメント(Swagger UI)
http://127.0.0.1:8000/docs
設定|FastAPI の基本セットアップとテストAPI
まずは、FastAPIの基本を理解するために、シンプルなAPIを作成します。
コード
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def read_root():
return {"message": "Hello, FastAPI!"}
説明
FastAPI()でアプリインスタンスを作成。@app.get("/")でルートエンドポイント/を定義し、シンプルなレスポンスを返す。read_root関数が実行されると{"message": "Hello, FastAPI!"}を返す。
実行方法
.venv\Scripts\activate
uvicorn main:app --reload
http://127.0.0.1:8000/
http://127.0.0.1:8000/docs
pip install fastapi uvicornを実行して FastAPI をインストール。uvicorn ファイル名:app --reloadでサーバー起動。- ブラウザで
http://127.0.0.1:8000/にアクセスすると"Hello, FastAPI!"が表示される。
小学生でも理解できるように説明してください。
小学生でもわかる FastAPI の使い方!
📌 FastAPI ってなに?
FastAPI(ファストエーピーアイ)は、Python というプログラミング言語で "Web アプリ" を作るための道具(フレームワーク)だよ!
とても速くて、使いやすいのが特徴なんだ。
🎯 ステップ 1: 基本のセットアップ
まず、「こんにちは!」 と言ってくれるシンプルなプログラムを作ってみよう!
📜 コード
from fastapi import FastAPI # FastAPI を使えるようにする
app = FastAPI() # アプリの本体を作る
@app.get("/") # 「/」にアクセスしたときにこの関数が動く
def read_root():
return {"message": "Hello, FastAPI!"} # これが画面に表示される
🔍 コードの説明
💡 どうやって動くの?
1️⃣ FastAPI() でアプリの本体を作る。(先生みたいなもの!)
2️⃣ @app.get("/") で「/」(トップページ)にアクセスされたら、下の関数を動かすよ!
3️⃣ read_root() という関数が動くと、「Hello, FastAPI!」 というメッセージを返すよ!
🚀 実行する方法
プログラムを動かすには、次の手順でやってみよう!
✅ 1. FastAPI をインストール
まず、FastAPI を使えるようにしよう。
(プログラムを作るための道具をインストールするイメージ!)
パソコンの「コマンドプロンプト」や「ターミナル」にこれを入力してね:
pip install fastapi uvicorn
👉 これで FastAPI がインストールされるよ!
✅ 2. プログラムを起動
プログラムを動かすには、次のコマンドを入力しよう:
uvicorn main:app --reload
👉 main:app は、main.py というファイルの app を動かすよ!
👉 --reload をつけると、変更をすぐに反映できるよ!
✅ 3. ブラウザで確認
プログラムが動いたら、パソコンのブラウザ(Google Chrome など)で
次のアドレスにアクセスしてみよう:
http://127.0.0.1:8000/
👉 すると、画面に {"message": "Hello, FastAPI!"} と表示されるよ! 🎉
✅ 4. API の説明を見る
FastAPI は 「API の説明書」 も自動で作ってくれるよ!
次のアドレスにアクセスすると、どんな API があるか確認できるよ:
http://127.0.0.1:8000/docs
👉 かっこいい画面が出てくるよ!✨
🎉 まとめ
✅ FastAPI を使うと、簡単に Web API を作れる!
✅ @app.get("/") でトップページを作る!
✅ uvicorn main:app --reload でプログラムを動かす!
✅ http://127.0.0.1:8000/ にアクセスすると、「Hello, FastAPI!」が見れる!
これが FastAPI の 「はじめの一歩」 だよ!
次は、もっと便利な API を作ってみよう! 🚀✨
設定|CORS(クロスオリジンリソース共有)の設定
フロントエンド(React)との通信を許可するためにCORS設定を追加します。
コード
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
# CORS 設定
app.add_middleware(
CORSMiddleware,
allow_origins=["http://localhost:3000"], # React のオリジンを許可
allow_credentials=True,
allow_methods=["*"], # すべてのHTTPメソッドを許可
allow_headers=["*"], # すべてのHTTPヘッダーを許可
)
@app.get("/")
def read_root():
return {"message": "Hello, FastAPI with CORS!"}
説明
CORSMiddlewareを追加し、allow_origins=["http://localhost:3000"]で React からのリクエストを許可。- これをしないと、ブラウザのCORS制約でAPIがブロックされる。
実行方法
.venv\Scripts\activate
uvicorn main:app --reload
http://127.0.0.1:8000/
http://127.0.0.1:8000/docs
小学生でも理解できるように説明してください。
🌍 CORS(コース)ってなに?小学生でもわかる説明!
🎯 ステップ 2: CORS を設定しよう!
CORS(コース) っていうのは、Webサイトが他のサイトとデータをやり取りできるようにするルールのことだよ!
🧐 なんで CORS が必要なの?
たとえば、こんなふうに Web アプリを作ったとするよ:
✅ フロントエンド(見た目の部分) → React(http://localhost:3000)
✅ バックエンド(データを管理する部分) → FastAPI(http://127.0.0.1:8000)
このとき、フロントエンド(React)からバックエンド(FastAPI)にデータを送ろうとすると、「えっ!違う場所(オリジン)からのリクエストはダメ!」 ってブラウザがブロックしちゃうんだ。💥🚫
🚀 CORS を設定すると…?
「このサイト(React)からのリクエストは OK にするよ!」 って FastAPI に教えてあげれば、通信ができるようになるよ!
📜 CORS を設定するコード
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware # CORSを使うための機能を追加
app = FastAPI()
# 🔥 CORS の設定を追加するよ!
app.add_middleware(
CORSMiddleware,
allow_origins=["http://localhost:3000"], # React からのリクエストをOKにする
allow_credentials=True, # 認証情報を許可(ログインなど)
allow_methods=["*"], # すべての HTTP メソッドを許可(GET, POST など)
allow_headers=["*"], # すべてのヘッダーを許可
)
@app.get("/")
def read_root():
return {"message": "Hello, FastAPI with CORS!"} # 「CORS を設定したよ!」と返す
🔍 コードの説明
1️⃣ from fastapi.middleware.cors import CORSMiddleware → CORS を使えるようにする!
2️⃣ app.add_middleware(CORSMiddleware, allow_origins=...) → どのサイトのリクエストをOKにするか 設定する!
3️⃣ allow_origins=["http://localhost:3000"] → React(フロントエンド)からのリクエストを許可!
4️⃣ allow_methods=["*"] → どんな方法(GET, POST など)でもOK!
5️⃣ allow_headers=["*"] → すべてのヘッダー(追加情報)もOK!
6️⃣ @app.get("/") → 「CORS を設定したよ!」と表示する API を作る!
⚡ 実際に動かしてみよう!
✅ 1. FastAPI を起動する
コマンドを入力して、FastAPI を動かそう!
uvicorn main:app --reload
👉 これで FastAPI が動き始めるよ! 🚀
✅ 2. API を確認する
ブラウザで次のアドレスにアクセスしてみよう:
http://127.0.0.1:8000/
👉 すると、こんなメッセージが見えるよ!
{"message": "Hello, FastAPI with CORS!"}
✅ 3. API の説明を確認
CORS の設定をした FastAPI の API を 自動で作られた説明ページ で見ることができるよ!
次のリンクにアクセスしてみてね:
http://127.0.0.1:8000/docs
👉 かっこいい API の説明ページ が出てくるよ!✨
🎉 まとめ
✅ CORS(コース) は「このサイトからのリクエストはOK!」って決めるルール
✅ CORS を設定しないと React(フロント)と FastAPI(バック)で通信できない!
✅ allow_origins=["http://localhost:3000"] で React の通信を許可!
✅ uvicorn main:app --reload で FastAPI を動かして確認!
これで フロントエンド(React)とバックエンド(FastAPI)が仲良く通信 できるようになったね! 🎉
次は データのやり取り をやってみよう! 🚀✨
データベース設定|データベースのセットアップ
SQLiteデータベースを作成し、データモデルを定義します。
コード
#モジュールインポート
from sqlmodel import SQLModel, create_engine
#データベースの設定
DATABASE_URL = "sqlite:///sales_info.db"
engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})
# データベース作成関数
def create_db_and_tables():
SQLModel.metadata.create_all(engine)
# アプリ起動時にデータベースを作成
@app.on_event("startup")
def on_startup():
create_db_and_tables()
説明
SQLModelは SQLAlchemy と Pydantic の機能を統合したORM。create_engineで SQLite データベースを作成。create_db_and_tables()でテーブルを作成。
小学生でも理解できるように説明してください。
📖 ステップ 3: データベースのセットアップを小学生でもわかるように説明!
💡 「データベース」って何?
データベースは、たくさんのデータをしまっておく「大きなノート」みたいなもの だよ!📖✨
たとえば、お店の売上やユーザーの情報を保存しておく場所だよ!
📝 何をするの?
1️⃣ データベースを作る
2️⃣ データベースにつなぐ(設定)
3️⃣ アプリを動かしたときにデータベースを準備する
🔍 コードを見てみよう!
# モジュールインポート
from sqlmodel import SQLModel, create_engine
# データベースの設定
DATABASE_URL = "sqlite:///sales_info.db" # データベースの名前を決める
engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False}) # データベースとつなぐ
# データベース作成関数
def create_db_and_tables():
SQLModel.metadata.create_all(engine) # テーブル(データを入れる箱)を作る
# アプリ起動時にデータベースを作成
@app.on_event("startup")
def on_startup():
create_db_and_tables() # アプリを動かすときにデータベースも作る
🧐 ひとつずつわかりやすく解説!
① モジュールインポート(道具を用意する)
from sqlmodel import SQLModel, create_engine
🔹 「SQLModel」 は、データベースを簡単に扱うための道具 🛠️
🔹 「create_engine」 は、データベースとつなぐために使う!🔌
② データベースの設定(どこにデータをしまう?)
DATABASE_URL = "sqlite:///sales_info.db"
🔹 データベースの名前を決める!
👉 "sales_info.db" という名前のデータベースを作るよ!💾
engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})
🔹 データベースとつながる準備をする!
③ データベースを作る関数
def create_db_and_tables():
SQLModel.metadata.create_all(engine)
🔹 データを入れるための「箱(テーブル)」を作る関数! 📦
④ アプリを動かしたときにデータベースも作る
@app.on_event("startup")
def on_startup():
create_db_and_tables()
🔹 「アプリをスタートするときに、データベースを作ってね!」という命令 🚀
✨ まとめ
✅ データベースは「大きなノート」📖(データを保存する場所!)
✅ 「create_engine」でデータベースとつながる🔌
✅ 「create_db_and_tables()」でテーブル(データを入れる箱)を作る📦
✅ アプリを動かしたときに、自動でデータベースを準備する!
これで小学生でもデータベースを作れるね!😆🎉
データベースモデル定義|ユーザーと売上データのモデル定義
データモデルを定義し、データを保存できるようにします。
コード
#モジュールインポート
from sqlmodel import SQLModel, Field
from typing import Optional
# モデル定義(SQLModel)
class User(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
username: str = Field(unique=True, index=True)
password: str # ハッシュ化されたパスワード
class Sales(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
year: int
department: str
sales: float
説明
table=Trueでデータベーステーブルとして定義。Field()を使ってidを主キーに設定。usernameはunique=Trueでユニーク制約をつけ、index=Trueで検索を高速化。Salesクラスで売上データ(年・部門・売上額)を管理。
小学生でも理解できるように説明してください。
📌 ステップ 4: ユーザーと売上データを保存する準備をしよう!
💡 そもそも「データモデル」って何?
データモデルとは、データを整理して保存するための「設計図」のことだよ!🏗️
たとえば、お店の売上(うりあげ)をメモするとき、
✅ 「何年のデータ?」
✅ 「どの部署(ぶしょ)のデータ?」
✅ 「いくらの売上?」
って決めてから書くよね?
コンピューターの中でも、こんなふうに 決められた形(モデル) にそってデータを保存するんだ!
🛠️ データモデルを作るコード
# 📦 必要な道具(モジュール)を準備
from sqlmodel import SQLModel, Field
from typing import Optional # ID を「なし」にできるようにするための道具
# 👤 ユーザーモデル(ログイン情報を保存)
class User(SQLModel, table=True): # 🗃️ 「User」 というデータの箱(テーブル)を作るよ!
id: Optional[int] = Field(default=None, primary_key=True) # 🔑 ID(番号)をつける
username: str = Field(unique=True, index=True) # 📛 ユーザー名(かぶらないようにする)
password: str # 🔒 パスワード(暗号化して保存)
# 📊 売上データのモデル
class Sales(SQLModel, table=True): # 🗃️ 「Sales」 というデータの箱(テーブル)を作るよ!
id: Optional[int] = Field(default=None, primary_key=True) # 🔑 ID(番号)をつける
year: int # 📅 何年のデータか?
department: str # 🏢 どの部署(ぶしょ)のデータか?
sales: float # 💰 売上の金額
🔍 コードの説明
👤 ユーザーモデル(User)
ログインする人の情報 を保存するよ!
✅ id: Optional[int] = Field(default=None, primary_key=True)
➡️ ID(番号)をつける!(1, 2, 3... みたいな感じ)
✅ username: str = Field(unique=True, index=True)
➡️ ユーザー名(名前)を保存する!(かぶらないようにする)
✅ password: str
➡️ パスワードを保存する!(暗号化する)🔒
📊 売上モデル(Sales)
売上のデータ を保存するよ!
✅ year: int
➡️ 何年のデータか?(たとえば 2025年)
✅ department: str
➡️ どの部署のデータか?(「営業部」とか「マーケティング部」とか)
✅ sales: float
➡️ いくら売れたか?(お金の金額)
📌 どうやって使うの?
1️⃣ ユーザー情報を保存!
👉 ユーザーを登録すると User に保存される!
(例)
| id | username | password |
|---|---|---|
| 1 | tanaka | **** |
| 2 | yamada | **** |
2️⃣ 売上データを保存!
👉 売上のデータが Sales に保存される!
(例)
| id | year | department | sales |
|---|---|---|---|
| 1 | 2024 | 営業部 | 1000000 |
| 2 | 2025 | 開発部 | 2000000 |
🎉 まとめ
✅ 「データモデル」 は、データを整理して保存する「設計図」🏗️
✅ 「User」 クラスで「ログインする人のデータ」を保存できる!👤
✅ 「Sales」 クラスで「売上データ」を保存できる!📊
これで データを保存する準備ができたよ! 🚀💾
次は データを実際に入れてみよう! 🎉
スキーマ|Pydantic スキーマの定義
リクエストデータのバリデーションを行うためのPydanticモデルを作成します。
コード
#モジュールインポート
from pydantic import BaseModel
# Pydanticスキーマ
class UserCreate(BaseModel):
username: str
password: str
class UserResponse(BaseModel):
id: int
username: str
class Config:
from_attributes = True # ORM モードを有効にする
class SalesCreate(BaseModel):
year: int
department: str
sales: float
説明
UserCreateはユーザー登録用リクエストスキーマ。UserResponseはユーザー情報のレスポンススキーマ。SalesCreateは売上データ登録用スキーマ。Config.from_attributes = Trueを指定すると、ORMオブジェクトをPydanticモデルに変換可能。
小学生でも理解できるように説明してください。
📌 ステップ 5: データの「正しい形」をチェックしよう!
💡 そもそも「バリデーション」って何?
バリデーションとは、データが「正しい形かどうか」をチェックすることだよ!✅
たとえば、
✖ 名前に数字が入っていたら?(123tanaka ❌)
✖ 売上データに「文字」が入っていたら?(売上: "たくさん" ❌)
こんなデータが入ると、コンピューターが困っちゃうよね!😵💥
そこで Pydantic(ピーダンティック) を使うと、こういう「おかしなデータ」を防ぐことができるんだ!🚦✨
🛠️ データの形を決めるコード
# 📦 必要な道具(モジュール)を準備
from pydantic import BaseModel # データの形を決めるための道具
# 👤 ユーザー登録用(データを受け取るとき)
class UserCreate(BaseModel):
username: str # 📛 ユーザー名(文字)
password: str # 🔒 パスワード(文字)
# 👤 ユーザー情報の返信用(データを送るとき)
class UserResponse(BaseModel):
id: int # 🔑 ユーザーのID(数字)
username: str # 📛 ユーザー名(文字)
class Config:
from_attributes = True # ORMのデータ(データベースの情報)を変換できるようにする
# 📊 売上データ登録用(データを受け取るとき)
class SalesCreate(BaseModel):
year: int # 📅 何年のデータか?(数字)
department: str # 🏢 どの部署のデータか?(文字)
sales: float # 💰 売上の金額(小数もOK)
🔍 コードの説明
👤 ユーザーのデータ
データを受け取るとき(UserCreate) ➡️ username(ユーザー名)と password(パスワード)が 文字 かチェックする!
データを送るとき(UserResponse) ➡️ id(ユーザーの番号)と username(名前)を 正しい形で送る!
💡 class Config: from_attributes = True
➡️ データベースから情報を取り出しやすくする設定!
📊 売上のデータ
データを受け取るとき(SalesCreate) ✅ year → 数字 じゃないとダメ!
✅ department → 文字 じゃないとダメ!
✅ sales → 小数の数字 じゃないとダメ!(例:100000.5)
💡 こうすることで、データが 間違った形で入るのを防ぐ ことができるよ!🚦✨
🎉 まとめ
✅ Pydantic を使うと「データの形」をチェックできる!🧐
✅ UserCreate / UserResponse / SalesCreate で、それぞれのデータの形を決める!📋
✅ **間違ったデータが入らないようにすることで、エラーを防げる!**🚀
これで データの安全管理ができるようになったよ! 🎉💾
次は、このデータを 実際に使ってみよう! 🎯
ユーティリティ(便利な)関数|パスワードのハッシュ化
パスワードを安全に保存するため、passlib を使ってハッシュ化します。
コード
#モジュールインポート
from passlib.context import CryptContext
# =====================
# 6. ユーティリティ関数(パスワードハッシュ & DBセッション)
# =====================
# セッション取得
def get_session():
with Session(engine) as session:
yield session
# パスワードハッシュ化の設定
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
# パスワードハッシュ化
def hash_password(password: str) -> str:
return pwd_context.hash(password)
# パスワード検証
def verify_password(plain_password: str, hashed_password: str) -> bool:
return pwd_context.verify(plain_password, hashed_password)
説明
bcryptを使ってパスワードをハッシュ化。hash_password()でパスワードを安全に保存。verify_password()でログイン時にパスワードを検証。
小学生でも理解できるように説明してください。
このコードを小学生でも理解できるように、わかりやすく説明しますね!😊
🔹 何をするコード?
- データベースを使う準備(
get_session) - パスワードを安全にする(
hash_password) - パスワードをチェックする(
verify_password)
💡 ①「データベースを使う準備」
def get_session():
with Session(engine) as session:
yield session
🔹 何をする?
👉 データベース(お店のレジみたいなもの)を開いて、データを入れたり、取り出したりするための「部屋」を準備します!
👉 yield session は、「データベースを使うよ!はいどうぞ!」って感じです。
💡 ②「パスワードを安全にする」
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
🔹 何をする?
👉 CryptContext という道具を使って、パスワードを「秘密のカギ」に変えられるようにします。
👉 bcrypt という方法で暗号化します。(例:"apple123" → "d8f$gk2@!#k3" みたいに変える)
💡 ③「パスワードを暗号化する」
def hash_password(password: str) -> str:
return pwd_context.hash(password)
🔹 何をする?
👉 入力されたパスワードを**秘密のカギ(暗号)**に変えます!
👉 こうすることで、誰かが見ても本当のパスワードがわからなくなります。
📝 例
print(hash_password("apple123"))
# 出力例:"$2b$12$yW7jGcL2yxXp2LnO1zK.xe0g7eXkjD9GpXh7wZ"
(本当のパスワードが見えない!)
💡 ④「パスワードをチェックする」
def verify_password(plain_password: str, hashed_password: str) -> bool:
return pwd_context.verify(plain_password, hashed_password)
🔹 何をする?
👉 入力されたパスワードが正しいかチェックします!
👉 plain_password(入力したパスワード)と hashed_password(暗号化されたパスワード)を比べて、
「正しければ True、間違ってたら False」 を返します!
📝 例
hashed_pw = hash_password("apple123")
print(verify_password("apple123", hashed_pw)) # ✅ 正しいので True
print(verify_password("orange456", hashed_pw)) # ❌ 間違いなので False
📝 まとめ
| 関数 | 何をする? | 例 |
|---|---|---|
get_session() | データベースの「部屋」を準備する | ー |
hash_password("apple123") | パスワードを暗号化する | "apple123" → "d8f$gk2@!#k3" |
verify_password("apple123", hashed_pw) | 入力したパスワードが正しいか確認する | True(合ってる) or False(間違い) |
これで、小学生でも自分でコードが書けるようになるはず!🚀✨
ユーザー関連のAPI|
コード
#モジュールインポート
from sqlmodel import Session, select
from fastapi import Depends, HTTPException
# =====================
# 7. ユーザー関連のAPI
# =====================
@app.post("/register/", response_model=UserResponse)
def register_user(user: UserCreate, session: Session = Depends(get_session)):
existing_user = session.exec(select(User).where(User.username == user.username)).first()
if existing_user:
raise HTTPException(status_code=400, detail="User already exists")
hashed_password = hash_password(user.password)
new_user = User(username=user.username, password=hashed_password)
session.add(new_user)
session.commit()
session.refresh(new_user)
return new_user
@app.post("/login/")
def login_user(user: UserCreate, session: Session = Depends(get_session)):
db_user = session.exec(select(User).where(User.username == user.username)).first()
if not db_user or not verify_password(user.password, db_user.password):
raise HTTPException(status_code=401, detail="Invalid username or password")
return {"message": "Login successful", "username": user.username}
説明
get_session()でデータベースセッションを取得。register_user()でユーザー登録。- ユーザー名が既に存在する場合は
400 Bad Requestを返す。
小学生でも理解できるように説明してください。
このコードを 小学生でも理解できる ように わかりやすく 説明します!😊
🔹 何をするコード?
- ユーザー登録(register_user) 👤✅
- 新しいユーザーをデータベースに保存する!
- すでに登録されている場合はエラーを出す!
- ユーザーログイン(login_user) 🔑💻
- ユーザー名とパスワードを確認して、正しければ「ログイン成功」と返す!
- もし間違っていたら「ログイン失敗」とする!
💡 ①「ユーザー登録(register_user)」
@app.post("/register/", response_model=UserResponse)
def register_user(user: UserCreate, session: Session = Depends(get_session)):
🔹 何をする?
👉 /register/ というURLにアクセスすると、新しいユーザーを登録 できる!
| 名前 | 説明 |
|---|---|
user: UserCreate | ユーザーの名前とパスワードを受け取る |
session: Session = Depends(get_session) | データベースを使う |
🔹 すでに登録済みかチェック
existing_user = session.exec(select(User).where(User.username == user.username)).first()
if existing_user:
raise HTTPException(status_code=400, detail="User already exists")
👉 session.exec(select(User).where(User.username == user.username)).first() は、
「この名前のユーザーがすでにいるか?」 をデータベースで探す!
🔴 すでにいたら → エラー (400 Bad Request)
🔹 パスワードを暗号化して登録!
hashed_password = hash_password(user.password)
new_user = User(username=user.username, password=hashed_password)
👉 hash_password(user.password) で パスワードを安全にする!
👉 new_user に 暗号化したパスワードで新しいユーザーを作る!
🔹 データベースに保存!
session.add(new_user)
session.commit()
session.refresh(new_user)
return new_user
✔ session.add(new_user) → 新しいユーザーをデータベースに入れる
✔ session.commit() → 保存ボタンを押す
✔ session.refresh(new_user) → 新しいデータを更新
✔ return new_user → 登録したユーザーを返す
📌 まとめると… 👉 すでに同じ名前のユーザーがいないなら、新しく登録する!
💡 ②「ユーザーログイン(login_user)」
@app.post("/login/")
def login_user(user: UserCreate, session: Session = Depends(get_session)):
🔹 何をする?
👉 /login/ にアクセスすると、ユーザーがログインできる!
👉 user: UserCreate で ユーザー名とパスワードを受け取る
👉 session: Session = Depends(get_session) で データベースを使う
🔹 データベースからユーザーを探す
db_user = session.exec(select(User).where(User.username == user.username)).first()
👉 select(User).where(User.username == user.username) で
「この名前のユーザーがいるか?」 を探す!
🔹 パスワードチェック
if not db_user or not verify_password(user.password, db_user.password):
raise HTTPException(status_code=401, detail="Invalid username or password")
👉 もしユーザーがいなかったら (not db_user) → エラー (401 Unauthorized)
👉 パスワードが間違っていたら (not verify_password(...)) → エラー (401 Unauthorized)
🔹 ログイン成功!
return {"message": "Login successful", "username": user.username}
✔ ユーザー名とパスワードが正しいなら…
✔ 「ログイン成功!」のメッセージを返す ✅
🔹 まとめ!
| 関数 | 何をする? |
|---|---|
register_user() | 新しいユーザーを登録する(パスワード暗号化) |
login_user() | ユーザー名とパスワードをチェックしてログインする |
🌟 実際に動かしてみると?
📝 ユーザー登録
📩 リクエスト
POST /register/
{
"username": "taro",
"password": "1234"
}
✅ レスポンス
{
"username": "taro",
"password": "$2b$12$yW7jGcL2yxXp2LnO1zK.xe0g7eXkjD9GpXh7wZ"
}
👉 パスワードが暗号化されている! 🔒
📝 ログイン
📩 リクエスト
POST /login/
{
"username": "taro",
"password": "1234"
}
✅ 成功!
{
"message": "Login successful",
"username": "taro"
}
🔴 間違えたら?
{
"detail": "Invalid username or password"
}
👉 パスワードを間違えるとエラー! 🚨
📌 まとめ(小学生向け)
このコードでできること!
- ユーザーを登録する!(すでに登録済みならエラー)
- パスワードを暗号化する!(見えないようにする)
- ユーザーがログインできる!(パスワードが合っていればOK)
これで、小学生でも 「ユーザー登録 & ログインAPI」 を理解できるはず!🚀✨
売上データ作成API (/sales/)作成
コード
# =====================
# 8. 売上データ関連のAPI
# =====================
@app.post("/sales/")
def create_sales(sales: SalesCreate, session: Session = Depends(get_session)):
existing_sales = session.exec(
select(Sales).where(
(Sales.year == sales.year) &
(Sales.department == sales.department) &
(Sales.sales == sales.sales)
)
).first()
if existing_sales:
raise HTTPException(status_code=400, detail="Duplicate sales data exists")
sales_data = Sales(
year=sales.year,
department=sales.department,
sales=sales.sales
)
session.add(sales_data)
session.commit()
session.refresh(sales_data)
return sales_data
@app.get("/sales/")
def read_all_sales(session: Session = Depends(get_session)):
sales_data = session.exec(select(Sales)).all()
if not sales_data:
raise HTTPException(status_code=404, detail="No sales data found")
return sales_data
@app.get("/sales/{year}")
def read_sales_by_year(year: int, session: Session = Depends(get_session)):
sales_data = session.exec(select(Sales).where(Sales.year == year)).all()
if not sales_data:
raise HTTPException(status_code=404, detail=f"No sales data found for year {year}")
return sales_data
説明
POSTリクエストでyear、department、salesを受け取る(SalesCreateモデルを使用)。get_sessionでデータベースセッションを取得。- 同じ
year、department、salesのデータが既に存在するかチェック。 - 重複データがある場合、
400 Bad Requestエラーを返す。 - 新しい売上データを作成し、データベースに保存。
- コミットして、作成した売上データを返す。
小学生でも理解できるように説明してください。
🔹 このコードは何をする?
これは 売上データを管理するAPI のコードです!💰📊
以下の3つの機能があります:
- 売上データを登録する(create_sales)
- 新しい売上データを追加する
- 同じデータがあればエラーを出す
- すべての売上データを取得する(read_all_sales)
- データベースにある売上データを全部見る
- データがないとエラーを出す
- 特定の年の売上データを取得する(read_sales_by_year)
- 指定した年の売上データだけを取得する
- データがなければエラーを出す
💡 ① 売上データを登録する(create_sales)
@app.post("/sales/")
def create_sales(sales: SalesCreate, session: Session = Depends(get_session)):
🔹 何をする?
👉 /sales/ に 売上データを送ると登録 できる!
👉 sales: SalesCreate で 年・部署・売上額を受け取る!
🔹 データがすでにあるかチェック
existing_sales = session.exec(
select(Sales).where(
(Sales.year == sales.year) &
(Sales.department == sales.department) &
(Sales.sales == sales.sales)
)
).first()
👉 「同じ年・部署・売上額のデータがある?」 を探す!
🔴 もしすでにデータがあったら?
if existing_sales:
raise HTTPException(status_code=400, detail="Duplicate sales data exists")
✔ 「すでに同じデータがあるよ!」 とエラーを出す! 🚨
🔹 新しい売上データを作成 & 保存
sales_data = Sales(
year=sales.year,
department=sales.department,
sales=sales.sales
)
session.add(sales_data)
session.commit()
session.refresh(sales_data)
return sales_data
✔ 新しいデータを作る!
✔ データベースに保存する!
✔ 登録したデータを返す!
📌 まとめると… 👉 売上データを追加する!(すでにある場合はエラー)
💡 ② すべての売上データを取得する(read_all_sales)
@app.get("/sales/")
def read_all_sales(session: Session = Depends(get_session)):
🔹 何をする?
👉 /sales/ にアクセスすると、全部の売上データを見れる!
🔹 データを取得する
sales_data = session.exec(select(Sales)).all()
✔ データベースにあるすべての売上データを取ってくる!
🔹 データがない場合はエラー
if not sales_data:
raise HTTPException(status_code=404, detail="No sales data found")
✔ データが1つもないなら「データがありません!」とエラーを出す! 🚨
🔹 データを返す
return sales_data
✔ データをそのまま返す! 📊✨
📌 まとめると… 👉 データベースにある売上データを全部見る!(なかったらエラー)
💡 ③ 特定の年の売上データを取得する(read_sales_by_year)
@app.get("/sales/{year}")
def read_sales_by_year(year: int, session: Session = Depends(get_session)):
🔹 何をする?
👉 /sales/2023 のように 年を指定して売上データを取得!
🔹 指定した年のデータを取得
sales_data = session.exec(select(Sales).where(Sales.year == year)).all()
✔ 「指定した年のデータだけを取ってくる!」
🔹 データがない場合はエラー
if not sales_data:
raise HTTPException(status_code=404, detail=f"No sales data found for year {year}")
✔ その年のデータがなかったら「データがないよ!」とエラーを出す! 🚨
🔹 データを返す
return sales_data
✔ データをそのまま返す! 🎯
📌 まとめると… 👉 特定の年の売上データを取得!(なかったらエラー)
🌟 実際に動かしてみる!
📝 1. 売上データを登録
📩 リクエスト
POST /sales/
{
"year": 2023,
"department": "IT",
"sales": 5000000
}
✅ レスポンス
{
"year": 2023,
"department": "IT",
"sales": 5000000
}
🔴 同じデータを登録しようとすると?
{
"detail": "Duplicate sales data exists"
}
👉 「同じデータがあるよ!」とエラーが出る! 🚨
📝 2. すべての売上データを見る
📩 リクエスト
GET /sales/
✅ レスポンス
[
{
"year": 2023,
"department": "IT",
"sales": 5000000
},
{
"year": 2022,
"department": "Marketing",
"sales": 3000000
}
]
👉 すべての売上データが見れる! 🎉
🔴 データがない場合?
{
"detail": "No sales data found"
}
👉 「データがないよ!」とエラーが出る! 🚨
📝 3. 2023年の売上データを見る
📩 リクエスト
GET /sales/2023
✅ レスポンス
[
{
"year": 2023,
"department": "IT",
"sales": 5000000
}
]
👉 2023年のデータだけ見れる! 🎯
🔴 データがない場合?
{
"detail": "No sales data found for year 2025"
}
👉 「その年のデータはないよ!」とエラーが出る! 🚨
📌 まとめ(小学生向け)
| 関数 | 何をする? |
|---|---|
create_sales() | 新しい売上データを登録する(同じデータならエラー) |
read_all_sales() | すべての売上データを取得する(データがなければエラー) |
read_sales_by_year() | 特定の年の売上データを取得する(なければエラー) |
これで、小学生でも 「売上データAPI」 を理解できるはず!🚀✨
動作確認
APIエンドポイントの動作確認
- http://127.0.0.1:8000/sales/
- http://127.0.0.1:8000/sales/2020
- http://127.0.0.1:8000/sales/2021
- http://127.0.0.1:8000/sales/2031
以下のように作成したAPIがそれぞれ表示されていれば、ひとまずOK。
最終コード:main.py
# =====================
# 1. 必要なモジュールをインポート
# =====================
from typing import Optional
from fastapi import FastAPI, Depends, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from sqlmodel import SQLModel, create_engine, Field, Session, select
from pydantic import BaseModel
from passlib.context import CryptContext
# =====================
# 2. アプリの設定(FastAPI & CORS)
# =====================
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=["http://localhost:3000"], # Reactのオリジン
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# =====================
# 3. データベース設定
# =====================
#データベースの設定
DATABASE_URL = "sqlite:///sales_info.db"
engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})
# データベース作成関数
def create_db_and_tables():
SQLModel.metadata.create_all(engine)
# アプリ起動時にデータベースを作成
@app.on_event("startup")
def on_startup():
create_db_and_tables()
# =====================
# 4. データベースモデル定義(SQLModel)
# =====================
# モデル定義(SQLModel)
class User(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
username: str = Field(unique=True, index=True)
password: str # ハッシュ化されたパスワード
class Sales(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
year: int
department: str
sales: float
# =====================
# 5. スキーマ(Pydantic)
# =====================
# Pydanticスキーマ
class UserCreate(BaseModel):
username: str
password: str
class UserResponse(BaseModel):
id: int
username: str
class Config:
from_attributes = True # ORMモードを有効にする
class SalesCreate(BaseModel):
year: int
department: str
sales: float
# =====================
# 6. ユーティリティ関数(パスワードハッシュ & DBセッション)
# =====================
# セッション取得
def get_session():
with Session(engine) as session:
yield session
# パスワードハッシュ化の設定
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
# パスワードハッシュ化
def hash_password(password: str) -> str:
return pwd_context.hash(password)
# パスワード検証
def verify_password(plain_password: str, hashed_password: str) -> bool:
return pwd_context.verify(plain_password, hashed_password)
# =====================
# 7. ユーザー関連のAPI
# =====================
@app.post("/register/", response_model=UserResponse)
def register_user(user: UserCreate, session: Session = Depends(get_session)):
existing_user = session.exec(select(User).where(User.username == user.username)).first()
if existing_user:
raise HTTPException(status_code=400, detail="User already exists")
hashed_password = hash_password(user.password)
new_user = User(username=user.username, password=hashed_password)
session.add(new_user)
session.commit()
session.refresh(new_user)
return new_user
@app.post("/login/")
def login_user(user: UserCreate, session: Session = Depends(get_session)):
db_user = session.exec(select(User).where(User.username == user.username)).first()
if not db_user or not verify_password(user.password, db_user.password):
raise HTTPException(status_code=401, detail="Invalid username or password")
return {"message": "Login successful", "username": user.username}
# =====================
# 8. 売上データ関連のAPI
# =====================
@app.post("/sales/")
def create_sales(sales: SalesCreate, session: Session = Depends(get_session)):
existing_sales = session.exec(
select(Sales).where(
(Sales.year == sales.year) &
(Sales.department == sales.department) &
(Sales.sales == sales.sales)
)
).first()
if existing_sales:
raise HTTPException(status_code=400, detail="Duplicate sales data exists")
sales_data = Sales(
year=sales.year,
department=sales.department,
sales=sales.sales
)
session.add(sales_data)
session.commit()
session.refresh(sales_data)
return sales_data
@app.get("/sales/")
def read_all_sales(session: Session = Depends(get_session)):
sales_data = session.exec(select(Sales)).all()
if not sales_data:
raise HTTPException(status_code=404, detail="No sales data found")
return sales_data
@app.get("/sales/{year}")
def read_sales_by_year(year: int, session: Session = Depends(get_session)):
sales_data = session.exec(select(Sales).where(Sales.year == year)).all()
if not sales_data:
raise HTTPException(status_code=404, detail=f"No sales data found for year {year}")
return sales_data