Slide 1

Slide 1 text

バックエンド研修/WebAPIサーバ作成 2025/05/08 サーチ事業部 中村 健⼈

Slide 2

Slide 2 text

本⽇の内容

Slide 3

Slide 3 text

© Geniee, Inc. 3 講義アジェンダ 1. 講義(1h) a. バックエンドサーバの役割 b. Web API c. APIフレームワーク d. アーキテクチャスタイル e. シナリオテスト 2. 課題(2h) 3. 昼休み 4. 課題(5h)

Slide 4

Slide 4 text

© Geniee, Inc. 4 研修のゴール 1. バックエンドサーバーの役割とWebAPIフレームワーク を⽤いた単純な実装⽅法を知る 2. テスト駆動でWebAPIサーバーを構築を開発する体験

Slide 5

Slide 5 text

バックエンドサーバーの役割

Slide 6

Slide 6 text

© Geniee, Inc. 6 1-a バックエンドサーバーの役割 バックエンドサーバーの役割 ~単純なCRUDを⾏うアプリケーションでもバックエンドサーバーが必要とされる理由~ ● クライアント側から機密情報を隠す ○ DBのアクセス情報 ○ 認証‧認可の実装 ● 難しい処理を実装することができる ○ SQLでは記述しにくい処理 ○ 複数のクライアントからのリクエストの処理 ● インターフェイスを固定化 ○ フロントエンドとバックエンドが疎結合になる ■ フロントの実装を変更なしに、DBの変更等を⾏うことができる ■ バックエンドサーバーから複数の外部サービスを呼び出すことができる 多分まだまだあるはず、

Slide 7

Slide 7 text

© Geniee, Inc. 7 1-a バックエンドサーバーの役割 必ずしもバックエンドサーバーが必要か? バックエンドサーバーを介さずにCRUDを⾏う例 (backend as a service とも⾒ることができる) ● Google Firebaseを利⽤したモバイルアプリ開発 ○ Google Firebase ■ フルマネージドなNoSQLデータベース ■ 簡単な認証‧認可まで⾏ってくれる ● Spabase ○ フルマネージドなPostgreSQL互換のデータベース ○ Firebaseと同様に簡単な認証‧認可まで⾏ってくれる モバイル(DOOH, SFAのみ) や toC(SWのみ) と相性が良いとされるが、弊社ではそんなに関係ないかも... ⼀番単純な使い⽅としては、デスクトップアプリのSQLiteをwebアプリに移⾏するために、クラウドに保存させる時に利⽤することとか、

Slide 8

Slide 8 text

Web API

Slide 9

Slide 9 text

© Geniee, Inc. 9 1-b Web API Web API(Web Application Programming Interface) ● Web APIとは、httpやhttpsなどを⽤いて実現されるAPI ○ ネットワークを通して提供される機能の規格のこと ○ 単純にAPIと略されることが多いが、ライブラリの使い⽅などもAPIの⼀種であり、これと区 別するため

Slide 10

Slide 10 text

© Geniee, Inc. 10 1-b Web API よく使われるWeb API の規格‧スタイル ● REST API (Representational State Transfer) ○ 最も⼀般的に使われるWeb APIの設計スタイル ○ APIの思想としてSOAP APIと対⽐されることもある。LINK ○ HTTP/1.1またはHTTP/2を使って通信し、リソース(例: /users, /posts)に対してGET, POST, PUT, DELETEなどのHTTPメソッドを使って操作 ○ データ形式は主にJSON(JavaScript Object Notation)(他にXMLなども可) ○ スキーマや型は任意のため、型安全性は別途管理が必要(OpenAPIを利⽤) ● gRPC ○ Googleが開発した⾼速なRPC(Remote Procedure Call)プロトコル ○ HTTP/2をベースに通信し、バイナリ形式を⽤いることで⾼速‧軽量 ○ マイクロサービス間通信に向いている ● tRPC ○ TypeScript製アプリケーションに特化 ○ 完全な型安全なAPI開発が可能 ○ ランタイムではJSONでデータをやりとりを⾏う

Slide 11

Slide 11 text

© Geniee, Inc. 11 1-b Web API Open API とは、 ● OpenAPI Specification(OAS) はREST-APIの標準仕様 ● ⾔語によらないHTTP APIの可視化のインターフェース記述⾔語の仕様 ● YAMLまたはJSONで記述される ● バージョン ○ 3.1.1 最新(2024/10/25)※課題では互換のために3.0.0にしている ● OpenAPIで扱うことで、Swaggerを使うことができる ○ 厳密にいうと、Swaggerの規格に合わせてOpneAPIが策定 参考: https://www.openapis.org/what-is-openapi

Slide 12

Slide 12 text

© Geniee, Inc. 12 1-b Web API そもそも、HTTPとは? ● WebブラウザとサーバーがTCP通信を使ってやり取りする通信プロトコル ● リクエストとレスポンスの形でデータを交換 構成要素 ● メソッド: ⽬的を⽰す動詞(例:GET, POST, PUT, DELETE) ● パス: /products/123 のようにリソースを表す ● クエリ: ?color=blue など、追加情報をURLで渡す ● ヘッダー: リクエストの付加情報(例:認証トークン) ● ボディ: 主にPOST/PUT時に送るデータ本体

Slide 13

Slide 13 text

© Geniee, Inc. 13 1-b Web API Swaggerとは ● OpenAPIの仕様で書かれたYAML/JSONファイルを扱うツール ● 簡単にいうと、GUIでhttpリクエストが投げられるツール(=curlのGUI版)

Slide 14

Slide 14 text

© Geniee, Inc. 14 1-b Web API Swaggerの利⽤ ● VScode/CursorにSwagger Viewerの拡張機能をインストール ○ 公式の拡張機能はないので、特に指定なし 参考: https://zenn.dev/nekoniki/articles/acd946cc349d1e

Slide 15

Slide 15 text

© Geniee, Inc. 15 1-b Web API Swaggerの利⽤ ● VScode/Cursorでsample.yamlを開く(本家) ● コマンドパレットでswaggerと⼊れて swaagger UI を使うみたいな項⽬を選択する ● 次のようなのが⾒えたらOK! 実際にリクエストを投げてみる ● POSTのタブを広げてTry it outを押下 ● パラメーターやボディーを⼊⼒できる ● Executeで実際にhttpリクエストが⾶ぶ ● 結果が表⽰される ○ デフォ値で200が返ってくるはずです

Slide 16

Slide 16 text

© Geniee, Inc. 16 1-b Web API ● yamlファイルを⾒て貰えばわかると思うのですが、⾏数がそれなりにあります ● フロントエンドなど、他のチームから使ってもらうためとは⾔っても、これをコードと⼀緒に⼿ 作業で作成‧保守するのは結構⼤変ですね... ● そうだ、コードから⾃動⽣成したらいいんだ! =>ハンドラ駆動と呼ばれるらしい ※逆にOpenAPIのyaml/jsonからコードを⽣成する⽅法もあります(=>スキーマ駆動と呼ばれるらしい) ※ツールが使いにくい場合は、⽣成AIを最⼤限活⽤するのは⼿段としてはあり

Slide 17

Slide 17 text

© Geniee, Inc. 17 1-b Web API OpenAPI クライアントライブラリの利⽤ ● OpenAPIクライアントライブラリを活⽤することで、yaml/jsonからコードを⽣成できる ○ const response = await fetch(url) みたいなコードが⽣成されるイメージ ○ 代表: Swagger Codegen ■ OpenAPIの公式 ■ javaの実⾏環境が必要 ■ 対応⾔語 ActionScript, Ada, Apex, Bash, C#, C++, Clojure, Dart, Elixir, Elm, Eiffel, Erlang, Go, Groovy, Haskell, Java, Kotlin, Lua, Node.js, Objective-C, Perl, PHP, PowerShell, Python, R, Ruby, Rust, Scala , Swift , Typescript Type script限定で⾔えば、openapi-typescript はjavaに依存しないので楽です。

Slide 18

Slide 18 text

APIフレームワーク

Slide 19

Slide 19 text

© Geniee, Inc. 19 1-c APIフレームワーク APIフレームワーク ● Web APIサーバーの作成に特化したフレームワーク ○ 例: Gin(Go), FastAPI(Python), Express(node.js), Axum(Rust) ○ フルスタックフレームワークをAPIモードで扱うことも可能 ■ Laravel(PHP), Rails(Ruby) ● ⾔語標準のhttpサーバーだと記述量が増えがちな部分をシンプルに保ってくれる枠組み ○ ⾔語によってはそもそもなかったり、⾮推奨だったりする ● 代表的なフレームワークだと、⼤体 OpenAPI の⾃動⽣成がある(プラグインの場合もある) ● フレームワークによって、ルーティングの記述⽅法だったり、パフォーマンスが異なる

Slide 20

Slide 20 text

© Geniee, Inc. 20 1-c APIフレームワーク APIフレームワーク(注意点) ● 今回の課題では⽐較的に扱いやすい Python の FastAPI を利⽤する。 ● FastAPIを例に説明を⾏うが、APIフレームワークでは基本的に同じような機能を持っている ● FastAPIの詳細についての説明はドキュメントを参照 ○ ⽇本語: https://fastapi.tiangolo.com/ja/

Slide 21

Slide 21 text

© Geniee, Inc. 21 1-c APIフレームワーク Pythonの環境構築 ● 今回は、pyenvとpoetryで環境を構築します ○ pyenv ■ Pythonの複数のバージョンを扱うため(今回は3.13.2) ■ グローバルでinstallしたpythonが3.13系なら不要かも ○ poetry ■ Pythonのプロジェクトのパッケージマネージャー ■ lockfileから環境を作成できるので、再現性が⾼い ● インストール⽅法(インストール済みの場合は不要) brew install pyenv brew install poetry

Slide 22

Slide 22 text

© Geniee, Inc. 22 1-c APIフレームワーク FastAPI を使ってみよう 1. 本⽇のディレクトリに移動 2. 環境の作成 pyenv install $(cat .python-version) pyenv local $(cat .python-version) poetry install 3. アプリの実⾏ poetry run python webapi/main.py

Slide 23

Slide 23 text

© Geniee, Inc. 23 1-c APIフレームワーク 今回の本筋とは少しずれますが、 Uvicorn: (語源: uvloop と ユニコーン) ● 軽量/⾼速なpythonのASGIサーバー ○ ASGI=Asynchronous Server Gateway Interface ■ https://asgi.readthedocs.io/en/latest/ ○ ⾮同期処理を⾏うwebサーバーとして機能 Gunicorn: (語源:緑のユニコーン) ● WSGI準拠のPythonアプリケーションを実⾏するためのサーバ ○ WSGI=Web Server Gateway Interface ■ 標準化されたインタフェース定義のこと ■ https://docs.python.org/ja/3.13/library/wsgiref.html ● Pythonの標準ライブラリのhttp.serverが⾮推奨なため ○ ※Gin(golang), express(node.js)は⾔語標準のhttpサーバーを利⽤可能

Slide 24

Slide 24 text

© Geniee, Inc. 24 1-c APIフレームワーク Uvicornの使い⽅ 1. コマンドラインから実⾏する⽅法コードに記述する⽅法 ● Dockerfileに書くと複雑になりがちな場合(本番とか) ● 分かりやすいので本⽇はこちらを利⽤ 2. コマンドラインから実⾏する⽅法 ● 確実にホットリロードを使いたい場合(1の⽅法で上⼿くできなかったら) ● uvicorn webapi.main:app --reload --port 8000 if __name__ =="__main__": uvicorn.run( “webapi.main:app", host="localhost", port=8080, reload=True, )

Slide 25

Slide 25 text

© Geniee, Inc. 25 1-c APIフレームワーク FastAPI を使ってみよう FastAPIのチュートリアルの抜粋で、APIフレームワークで普遍的な機能の紹介 ①ルートの記述 ②パスパラメータ ③クエリパラメーター ④リクエスト、レスポンスボディ ⑤ヘッダー ⑥ミドルウェア ⑦OpenAPIのjsonの⽣成 ⑧Swagger UI の確認 ⑨Redocの確認

Slide 26

Slide 26 text

© Geniee, Inc. 26 1-c APIフレームワーク FastAPI を使ってみよう ①ルートの記述 初期化を⾏なった後に、そのパスで⾏いたい処理が書かれた関数に のようにデコレーターでパスを記述する以下同様 @app.post, @app.put, @app.delete @app.options, @app.head, @app.patch, @app.trace @app.get("/") async def root(): # async関数でも通常の関数でも可 return {"message": "Hello World"} from fastapi import FastAPI app = FastAPI()

Slide 27

Slide 27 text

© Geniee, Inc. 27 1-c APIフレームワーク FastAPI を使ってみよう ②パスパラメータ パラメータとして当てはめたい部分を{}で記述する from fastapi import FastAPI app = FastAPI() @app.get("/items/{item_id}") async def read_item(item_id): return {"item_id": item_id}

Slide 28

Slide 28 text

© Geniee, Inc. 28 1-c APIフレームワーク FastAPI を使ってみよう ③クエリパラメーター パスパラメータではない関数パラメータを宣⾔すると、それらは⾃動的に "クエリ" パラメータとして解釈される from fastapi import FastAPI app = FastAPI() fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}] @app.get("/items/") async def read_item(skip: int = 0, limit: int = 10): return fake_items_db[skip : skip + limit]

Slide 29

Slide 29 text

© Geniee, Inc. 29 1-c APIフレームワーク FastAPI を使ってみよう ④リクエスト、レスポンスボディ パスパラメータではない関数パラメータをpydacticでモデルを定義して宣⾔する Pydantic ● データバリデーションライブラリ ● データの構造と型を明確に定義 => open API にも反映される ● バリデーションに失敗した場合、⾃動的にエラーレスポンスを⽣成

Slide 30

Slide 30 text

© Geniee, Inc. 30 1-c APIフレームワーク from typing import Union from fastapi import FastAPI from pydantic import BaseModel class Item(BaseModel): # BaseModelを継承しリクエスト・レスポンスボディを定義 name: str description: Union[str, None] = None price: float tax: Union[float, None] = None app = FastAPI() @app.post("/items/") async def create_item(item: Item): # 定義したクラスのインスタンスを引数とする return item

Slide 31

Slide 31 text

© Geniee, Inc. 31 1-c APIフレームワーク FastAPI を使ってみよう リクエスト、レスポンスボディ Pydanticを使わない⽅法もあります。 リクエストボディについては⾮推奨 レスポンスボディについては @app.get("/items/", response_model=List[str]) のように定義することも可能 (サンプルコードにも⼀部使ってます。) 参考: https://fastapi.tiangolo.com/ja/tutorial/body-multiple-params/#_2

Slide 32

Slide 32 text

© Geniee, Inc. 32 1-c APIフレームワーク FastAPI を使ってみよう ⑤ヘッダー 関数の引数の型をHeaderとして定義する from typing import Union from fastapi import FastAPI, Header app = FastAPI() @app.get("/items/") async def read_items(user_agent: Union[str, None] = Header(default=None)): return {"User-Agent": user_agent}

Slide 33

Slide 33 text

© Geniee, Inc. 33 1-c APIフレームワーク FastAPI を使ってみよう ⑥ミドルウェア ● 複数のパスで同じ処理をパスの処理の前後に⾏うときに利⽤(例:認証, 計測) ● ※Apache / Nginxなどのミドルウェアとはレイヤーが異なる ● @app.middleware("http")でミドルウェアを追加することができる import time from fastapi import FastAPI, Request app = FastAPI() @app.middleware("http") async def add_process_time_header(request: Request, call_next): start_time = time.perf_counter() response = await call_next(request) process_time = time.perf_counter() - start_time response.headers["X-Process-Time"] = str(process_time) return response

Slide 34

Slide 34 text

© Geniee, Inc. 34 1-c APIフレームワーク FastAPI を使ってみよう ⑦OpenAPIのjsonの⽣成 ローカルに保存 curl http://localhost:8000/openapi.json -o openapi.json ※もしjqをインストールしているなら(エディタにフォーマッターがあればそれでも良い) curl http://localhost:8000/openapi.json | jq > openapi.json jsonのファイルの内容を確認 ● FastAPIで登録したパスについての説明がある

Slide 35

Slide 35 text

© Geniee, Inc. 35 1-c APIフレームワーク FastAPI を使ってみよう ⑧Swagger UI の確認 http://localhost:8000/docs にアクセスして Swagger UIが⾒れることを確認 ※使い⽅は先ほどのものと同様

Slide 36

Slide 36 text

© Geniee, Inc. 36 1-c APIフレームワーク FastAPI を使ってみよう ⑨Redocの確認 https://localhost:8000/redoc にアクセスして Redocが⾒れることを確認 Redocの特徴 ● Swagger UIのようにリクエストを投げることはできない ● レスポンス形式がネストしている時に⾒やすい ● 静的なhtmlとして出⼒が可能 (⭕AWS S3等で共有が容易に) ○ 1ファイルにまとめるには少し⼯夫が必要 (以下参考までに) npm install -g redoc-cli curl http://localhost:8000/openapi.json -o openapi.json npx redoc-cli bundle openapi.json -o redoc.html

Slide 37

Slide 37 text

アーキテクチャスタイル

Slide 38

Slide 38 text

© Geniee, Inc. 38 1-d アーキテクチャスタイル バックエンドで APIの定義ができたら、DBなどからデータをどう取得するか DBのアクセスそのものはDB研修で⾏ったはずだが、DBアクセスの関数はどう呼び出すか 話すと⻑くなるから、簡単に 3層アーキテクチャ(レイヤードアーキテクチャ) ● プレゼンテーション層/アプリケーション層/データ層 の3層 ● データ →アプリケーション → プレゼンテーション の依存関係 ● ⼩さいプロジェクトではコードがシンプルになる オニオンアーキテクチャ ● データの整合性にDBのテーブル定義では無くコード上で強いルールを課したい場合に有効 ● プレゼンテーション層/ユースケース層/インフラ層/ドメイン層 ● 依存注⼊(DI)を⾏うことで、コードの⼤部分がDBに依存しない形にできる ○ 外の層は中の層のみ import (use, include, require) できる プレゼンテーション層 アプリケーション層 データ層

Slide 39

Slide 39 text

シナリオテスト

Slide 40

Slide 40 text

© Geniee, Inc. 40 1-e シナリオテスト Swagger UIで動作確認ができると⾔っても、⾃動化したいよね... シナリオテスト(APIシナリオテスト)とは、 ● 統合テストと読み替えられる場合もある ● 動作させているAPIサーバーに対して、複数のAPIを連続的に呼び出して実⾏するテストケース ● APIサーバーに依存させずに実⾏することで、⾔語のテストよりも1階層上で実⾏するイメージ ● 例: よくあるユーザー管理のエンドポイントで、 1. ユーザーAの作成 =>作成成功 2. ユーザーAで⾃分の情報を取得 => 取得成功 3. ユーザーAで⾃分の情報を更新 => 更新成功 4. ユーザーAで更新された情報が取得できる => 取得成功 5. ユーザーBでは何も⾒えない => 未認可のユーザー

Slide 41

Slide 41 text

© Geniee, Inc. 41 1-e シナリオテスト ● 例: よくあるユーザー管理のエンドポイントで、 1. ユーザーAの作成 =>作成成功 2. ユーザーAで⾃分の情報を取得 => 取得成功 3. ユーザーAで⾃分の情報を更新 => 更新成功 4. ユーザーAで更新された情報が取得できる => 取得成功 5. ユーザーBでは何も⾒えない => 未認可のユーザー ● 実現⽅法 ○ 各⾔語のHTTPライブラリで実装 ■ 例: bash scriptとcurl ○ Postman(Newman): LINK ○ Karate: LINK ○ runn: LINK ○ k6: LINK (負荷テストツールを流⽤)

Slide 42

Slide 42 text

© Geniee, Inc. 42 1-e シナリオテスト シナリオテストのメリット 1. UIテストと⽐べて実⾏スピードが速い 2. テストの安定性が⾼い 3. ⾃動テストへの組込みが容易 4. 負荷テストとの相性も良い(実際にDBを呼び出すこともできる) 参考: https://www.docswell.com/s/katzumi/5EN8N1-10-reasons-to-write-api-scenario-tests

Slide 43

Slide 43 text

© Geniee, Inc. 43 1-e シナリオテスト シナリオテスト(=統合テスト)を重視する考え ● Testing Trophy という考え⽅に基づいてる React Testing Libraryの開発者であるKent C. Dodds⽒が提唱している、どのテ ストを重視すべきであるかをトロフィーの形で表現した考え⽅ ● Integrationが最もコスト/速度と信頼性のバランスに優れており、テスト実装の 労⼒の⼤半はここに費やすべき 参考:https://kentcdodds.com/blog/the-testing-trophy-and-testing-classifications

Slide 44

Slide 44 text

© Geniee, Inc. 44 1-e シナリオテスト k6 の記述⽅法 jsで記述するけど中⾝はgolang DB接続にはプラグインが必要 今回はgolangのプロジェクトでないので、プラグインは使わずにscriptで代⽤ ● javascript で記述 ● http.get()で取得 ● check()で値を検証

Slide 45

Slide 45 text

© Geniee, Inc. 45 1-e シナリオテスト k6 の実⾏⽅法 今回の基礎課題ではテストを実⾏するだけ k6 run test_scenario/work_00.js を実⾏すると右のようになる 負荷テストのツールなので⾊々な値が⾒れ ますが、 check succeeded が 100.00% になったら成功