Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Laravel で API バージョニングを実装するなら
Search
Shohei Okada
September 26, 2018
Programming
1
35
Laravel で API バージョニングを実装するなら
2018-09-26 開催の「第130回 PHP勉強会@東京」におけるLT資料です
https://phpstudy.doorkeeper.jp/events/79982
Shohei Okada
September 26, 2018
Tweet
Share
More Decks by Shohei Okada
See All by Shohei Okada
たった 1 枚の PHP ファイルで実装する MCP サーバ / MCP Server with Vanilla PHP
okashoi
1
410
どうして手を動かすよりもチーム内のコードレビューを優先するべきなのか
okashoi
2
1.5k
パスワードのハッシュ、ソルトってなに? - What is hash and salt for password?
okashoi
3
250
設計の考え方 - インターフェースと腐敗防止層編 #phpconfuk / Interface and Anti Corruption Layer
okashoi
11
4.2k
"config" ってなんだ? / What is "config"?
okashoi
0
1.4k
ファイル先頭の use の意味、説明できますか? 〜PHP の namespace と autoloading の関係を正しく理解しよう〜 / namespace and autoloading in php
okashoi
4
1.7k
MySQL のインデックスの種類をおさらいしよう! / overviewing indexes in MySQL
okashoi
0
980
PHP における静的解析(あるいはそもそも静的解析とは) / #phpcondo_yasai static analysis for PHP
okashoi
1
660
【PHPカンファレンス沖縄 2023】素朴で考慮漏れのある PHP コードをテストコードとともに補強していく(ライブコーディング補足資料) / #phpcon_okinawa 2023 livecoding supplementary material
okashoi
3
2k
Other Decks in Programming
See All in Programming
The Past, Present, and Future of Enterprise Java with ASF in the Middle
ivargrimstad
0
120
デザイナーが Androidエンジニアに 挑戦してみた
874wokiite
0
470
The Past, Present, and Future of Enterprise Java
ivargrimstad
0
370
Ruby×iOSアプリ開発 ~共に歩んだエコシステムの物語~
temoki
0
320
Android 16 × Jetpack Composeで縦書きテキストエディタを作ろう / Vertical Text Editor with Compose on Android 16
cc4966
1
230
Laravel Boost 超入門
fire_arlo
3
220
Improving my own Ruby thereafter
sisshiki1969
1
160
rage against annotate_predecessor
junk0612
0
170
Processing Gem ベースの、2D レトロゲームエンジンの開発
tokujiros
2
130
Testing Trophyは叫ばない
toms74209200
0
880
モバイルアプリからWebへの横展開を加速した話_Claude_Code_実践術.pdf
kazuyasakamoto
0
330
MCPとデザインシステムに立脚したデザインと実装の融合
yukukotani
4
1.4k
Featured
See All Featured
Building an army of robots
kneath
306
46k
Cheating the UX When There Is Nothing More to Optimize - PixelPioneers
stephaniewalter
285
13k
For a Future-Friendly Web
brad_frost
180
9.9k
What’s in a name? Adding method to the madness
productmarketing
PRO
23
3.7k
Responsive Adventures: Dirty Tricks From The Dark Corners of Front-End
smashingmag
252
21k
KATA
mclloyd
32
14k
Agile that works and the tools we love
rasmusluckow
330
21k
Build The Right Thing And Hit Your Dates
maggiecrowley
37
2.9k
Put a Button on it: Removing Barriers to Going Fast.
kastner
60
4k
A designer walks into a library…
pauljervisheath
207
24k
The Success of Rails: Ensuring Growth for the Next 100 Years
eileencodes
46
7.6k
The Cult of Friendly URLs
andyhume
79
6.6k
Transcript
API バージョニング を Laravel で実装するなら 第130回 PHP勉強会@東京
岡田 正平(おかだ しょうへい)@okashoi • 株式会社ウィルゲート 2015年新卒入社 • 開発室 ソリューションユニット 所属
• PHP, Laravel, Vue.js 2 自己紹介 Slides:
クライアントに対し、古い形式でのアクセス方法を提供しつづける 例)Google Maps Embed API https://www.google.com/maps/embed/v1/place?q=...&key=... 新しいアクセス方法・レスポンス形式などは v2, v3… としていく
※他にもメディアタイプでバージョンを指定する方法などがある 3 API バージョニング
クライアントに対し、古い形式でのアクセス方法を提供しつづける 例)Google Maps Embed API https://www.google.com/maps/embed/v1/place?q=...&key=... 新しいアクセス方法・レスポンス形式などは v2, v3… としていく
※他にもメディアタイプでバージョンを指定する方法などがある 4 API バージョニング このスライドではいったん この方法で考えます
愚直に考えるとこう routes/api.php 5 Laravel でどう実装する?① Route::get('v1/posts', 'V1/PostController@index'); Route::get('v2/posts', 'V2/PostController@index');
愚直に考えるとこう routes/api.php 6 Laravel でどう実装する?① Route::get('v1/posts', 'V1/PostController@index'); Route::get('v2/posts', 'V2/PostController@index'); バージョンごとに
異なる Controller 明示的に指定
たしかに実現できてるけど…… • 新しいバージョンが増えるたびに、全てを追記するの? ➢ バージョンを扱う部分は仕組み化したい • コントローラを分割してしまうとコピペが横行するおそれアリ ➢ コントローラ内の条件分岐にしたい 7
Laravel でどう実装する?①
8 Laravel でどう実装する?① 参考)http://kenn.hatenablog.com/entry/2014/03/06/105249
改善案:ルーティングパラメータとして渡す 9 Laravel でどう実装する?② Route::get('{version}/posts', 'PostController@index') ->where('version', 'v[1-9][0-9]*'); routes/api.php public
function index(string $versionString, Request $request) { // 'v1' => 1 のように int 値に変換 $version = (int)substr($versionString, 1); if ($version === 1) { // v1 の処理 } // ... 以下略 } apps/MyApp/Application/Http/Controllers/PostConroller.php
改善案:ルーティングパラメータとして渡す 10 Laravel でどう実装する?② Route::get('{version}/posts', 'PostController@index') ->where('version', 'v[1-9][0-9]*'); routes/api.php public
function index(string $versionString, Request $request) { // 'v1' => 1 のように int 値に変換 $version = (int)substr($versionString, 1); if ($version === 1) { // v1 の処理 } // ... 以下略 } apps/MyApp/Application/Http/Controllers/PostConroller.php
改善案:ルーティングパラメータとして渡す 11 Laravel でどう実装する?② Route::get('{version}/posts', 'PostController@index') ->where('version', 'v[1-9][0-9]*'); routes/api.php public
function index(string $versionString, Request $request) { // 'v1' => 1 のように int 値に変換 $version = (int)substr($versionString, 1); if ($version === 1) { // v1 の処理 } // ... 以下略 } apps/MyApp/Application/Http/Controllers/PostConroller.php パターン指定による制限
改善案:ルーティングパラメータとして渡す 12 Laravel でどう実装する?② Route::get('{version}/posts', 'PostController@index') ->where('version', 'v[1-9][0-9]*'); routes/api.php public
function index(string $versionString, Request $request) { // 'v1' => 1 のように int 値に変換 $version = (int)substr($versionString, 1); if ($version === 1) { // v1 の処理 } // ... 以下略 } apps/MyApp/Application/Http/Controllers/PostConroller.php
①の問題点は解決できたものの、欲を言うなら • 毎 action で ←これやるの? ➢ 勝手に変換されていて欲しい • による制限が
に対してできない ➢ パターン指定は一箇所だけにしたい 13 Laravel でどう実装する?② $version = (int)substr($versionString, 1); ->where() Route::group() // できそうでできない Route::group(['prefix' => '{version}'], function () { Route::get('posts', 'PostController@index'); Route::get('users', 'UserController@index'); })->where('version', 'v[1-9][0-9]*');
None
アイディア:Explicit Route Model Binding を使う 15 Laravel でどう実装する?③ カスタマイズロジックを用いれば Eloquent
ORM 以外も Bind できる
1. API バージョンを表現するクラス (Value Object)を作る 2. RouteServiceProvider で Binding を(ついでに
pattern も)登録 3. Controller で利用する 16 Laravel でどう実装する?③
1. API バージョンを表現するクラス (Value Object)を作る 2. RouteServiceProvider で Binding を(ついでに
pattern も)登録 3. Controller で利用する 17 Laravel でどう実装する?③ class ApiVersion { /** * @var int */ protected $value; /** * @param string $versionString */ public function __construct(string $versionString) { $this->value = (int)substr($versionString, 1); } /** * @return int */ public function value(): int { return $this->value; } /** * @param int $number * @return bool */ public function is(int $number): bool { return $this->value === $number; } }
1. API バージョンを表現するクラス (Value Object)を作る 2. RouteServiceProvider で Binding を(ついでに
pattern も)登録 3. Controller で利用する 18 Laravel でどう実装する?③ public function boot(): void { Route::pattern('version', 'v[1-9][0-9]*'); parent::boot(); Route::bind('version', function ($versionString, $route) { return new ApiVersionValue($versionString); }); } tips: は より前 は より後 でないと正しく動かない Route::pattern() parent::boot() Route::bind() parent::boot()
1. API バージョンを表現するクラス (Value Object)を作る 2. RouteServiceProvider で Binding を(ついでに
pattern も)登録 3. Controller で利用する 19 Laravel でどう実装する?③ Route::get('{version}/posts', 'PostController@index'); public function index(ApiVersion $version, R... { // 特定のバージョンかどうか調べるには // $version->is() を使用 if ($version->is(1)) { // v1 の処理 } // バージョンの値を得たければ // $version->value() を使用 if ($version->value() <= 3) { // v2, v3 の処理 } // 以下略 } ↓
• Explicit Route Model Binding は Model 以外にも使い道がある! • API
バージョンを Value Object にするは良い工夫だと思う(自画自賛) • Application Layer の Value Object になる(特殊な例?) • (余談)そもそも API バージョニングって難しい 20 所感など
• 本当にその API にバージョンニングいる? • SSKDs (Small Set of Known
Developers) 向けならいらない? • c.f.) LSUDs (Large Set of Unknown Developers) • 後から足すことになるくらいなら、当面 v1 であってもひとまず…… • どのタイミングでバージョンを変える? • 後方互換が保てなくなったら?時間(1 年ごとなど)で区切る? • エンドポイント単位で更新?API 全体で更新? • バージョン指定なしを許容するか?許容する場合の挙動はどうするか? 21 余談:そもそも API バージョニング難しい問題 などなど……