(REST)Web API 入門
最近、APIの実装を実務でやっているのですが、少し理解が曖昧な部分があるなと思ってました。
なので、今回は勉強がてらまとめたWeb APIについて入門的なことを解説していきます。
そもそもRESTとは何か、APIとは何か、どのように設計すれば良いのかなど包括的に解説していくので、参考になるかと思います。
Web APIとは
そもそもAPIとはApplication Programming Interfaceの略になります。
要はプログラム間でデータをやり取りできるようにした、インターフェースです。
ただ、文脈によってAPIには次の意味も含まれることがあります。
- プログラム間のルールを設定した規約
- データをやり取りするためのサービス自体
このように、APIという単語は少しややこしいです。
なのでAPIとは、「ここのURLに、この形でリクエストを投げれば、このような結果が返ってくる、という風に決まっているサービス」みたいな理解で良いかと思います。
このAPIを使用することで、言語問わずデータの受け渡しが可能になったりします。
例えば、Twitter APIを使うことでどの言語でもTweetを取得できたりします。
また、Webサービスとモバイルアプリ両方から利用できるようにすることも可能です。
RESTとは
次に、APIとよく一緒に出てくるRESTについて解説していきます。
RESTはREpresentational State Transferの略になります。
よくわからないと思いますが、これはWebサービスの設計モデルです。
そして、このRESTの原則を守って設計されたAPIをRESTful APIと呼びます。
RESTful APIとは
先ほど、RESTful APIについて説明しましたが、1つ問題があります。
それは、RESTの原則は結構昔に提唱されたものなので、今の時代と合わないことがことが多いことです。
なので、RESTの原則の6つの中でも主に次の3つ守っているものが、RESTful APIと呼ばれることが多いです。
(ここは賛否両論あるかと思います。)
- アドレス可能性
- 統一インターフェース
- ステートレス
それぞれ具体的に解説してきます。
アドレス可能性
アドレス可能性とは、要は「指定するURLが操作するリソースを表すよ」という意味になります。
例えば、https://qiita.com/api/v2/users
のURLはユーザーというリソース表すURLになります。
そして、https://qiita.com/api/v2/items
は記事というリソースを表します。
このようにREST APIでは、URLによってどのリソースにアクセスするか判断するように設計されています。
ちなみに、ユーザーを”取得”するか、”更新”するかなどは後ほど紹介するHTTPメソッドによって決まるので、/getUsersみたいなURLはNGになります。
統一インターフェース
統一インターフェースとは、決まった通信方式でやり取りをするという決まりになります。
最近のWebサービスは、ほとんどがHTTPメソッドによってやり取りが行われます。
ちなみに、データ形式としては基本的にJSONが使われます。
そして、この決まったHTTPメソッドによってリソースへの操作を指定することになります。
例えば、HTTPメソッドであれば次のようになります。
- GET: リソースを取得する
- POST: 新しいリソースを作成する
- PUT: 既存のリソースを更新する
- DELETE: リソースを削除する
このように、どのような方法でやり取りするかが決まっていることを統一インターフェースと呼びます。
ステートレス
ステートレスとは、API側でクライアント側の状態を保持しないという意味になります。
例えば、今Aというユーザーがログイン中だという情報を保持しないことになります。
逆に、ログイン中などの状態を実現したい時は、cookieにsessionIdなどを持たせたりすることで実現できます。
こうすることで、コードがシンプルになるなどの利点があります。
Web APIの仕組み
次に、Web APIがどのように動いているのかということを解説していきます。
まず、クライアントがTwitterなどのwebサービスにアクセスした場合を考えてみましょう。
(あくまで例えなので、実際のTwitterとは動きが違う箇所もあります)
まず、twitterのURLを入力するなりして、Twitterにアクセスします。
そして、アクセスするタイミングでクライアントからTwitterのWebサーバーへ、トップページのHTMLが欲しいとリクエストを投げます。
次にWebサーバーが、そのHTMLをクライアントに返し、ブラウザがそのHTMLを表示してくれます。
これがいわゆる静的サイトと呼ばれているサイトの仕組みです。
図にすると、次のようになります。
では、ツイートするという時はどうなるでしょうか?
まず、クライアントがツイートボタンを押し、Webサーバーへリクエストを投げます。
そして、Webサーバーではツイートの処理ができないので、リバースプロキシというものを使い、アプリケーションサーバーへリクエストを渡します。
アプリケーションサーバで必要な処理を行い、実際にデータベースにツイートを保存するためにDBサーバーにデータの登録を依頼します。
そして、問題なければうまくいったことをDBサーバー⇨アプリケーションサーバー⇨Webサーバー⇨クライアントと伝えます。
これが一般的なWebサービスの流れになります。
そして、このように役割ごとに3つのサーバーに分けていることをWebアプリケーションの3層構造と呼びます。
図にすると、以下のようになります。
最後に、Web APIを使った場合を考えましょう。
この場合は、ツイートするという処理をしたい時は、https://api.twitter/tweets (URLは適当です)にPUTメソッドを送ることになります。
そして、返ってきたデータを元にホーム画面の一番上にツイート内容が表示されることになります。
ちなみに、このようにJSONを受け取り、それをもとにページを書き換える方式をAjaxと呼びます。
図にすると、次のようになります。
ちなみに、この方式だとページのリロードなしに、ページの内容を更新することが可能です。
なので、SPAなどでよく使われる方式となります。
セキュリティ
最後にセキュリティ関連についていくつか解説していきます。
認証と認可
Webサービスにおいては認証と認可という考え方がありまう。
簡単に言うと、 次のような感じです。
- 認証=本人の特定
- 認可=アクセス許可
そして、これを実現するためにJWTというものを使われることが多いです。
具体的な内容については、この記事が分かりやすかったので参考にどうぞ。
バリデーション
ブラウザ側で何かサーバー側にリクエストをしたい場合、フロントエンドでも入力内容にバリデーションをかけることが多いかと思います。
ただ、curlコマンドを使ったり開発者ツールを使ったりすると、その穴をすり抜けることが可能となります。
なので、基本的にバックエンド側でもバリデーションをかけるようにしましょう。
CORS
CORSとは、Cross Origin Resource Sharingの略になります。
これは、あるオリジンから取得したJSは、別のオリジンのデータを読み込めないというルールになります。
例えば、qiita.com から取得したJSで、youtube.comにリクエストを投げることはできません。
もし、このように他のオリジンからのアクセスを許可したい場合は、サーバー側でその設定をする必要があります。
WebAPIの設計方法
最後に、Web APIの設計方法について簡単に解説していきます。
まず、APIを設計する際はしっかりと図などを事前に作成しておくと良いです。
例えば、以下のような図があると実装がスムーズに進みます。
- ユースケース図(想定されるユースケースを図にする)
- 概念図(必要となりそうなクラスとそれらの関係を図にする)
- ER図(DB設計をする)
これらの図を書くことで、ある程度どのように実装していこうかの方針が定まってくるかと思います。
その後に考えるべきなのが、アーキテクチャー設計です。
要は、コードをどのようなディレクトリやファイル群に分け、どのような依存関係を持たせるかを決めることです。
また、アーキテクチャー設計の際は、3つの観点から考えると分かりやすいです。
まず1つ目は、どのように層を分けるかです。
有名な設計方法だと
3層アーキテクチャーやMVCなどがあります。
2つ目が、層の中をどのように分けるかです。
例えば、ビジネスロジック層はトランザンクションスクリプトで実装するのか、ドメインモデルで実装するのかなどです。
そして3つ目は、それぞれの層にどのような依存関係を持たせるかです。
これは、クリーンアキテクチャーやオニオンアーキテクチャなどがあります。
ちなみに、この設計方法が優れているなどはなく、目的やサービス内容によって適している設計手法は変わってきます。
重要なのは、役割分担や依存関係がハッキリとしていることです。
DI
一応、依存関係周りで知っておいた方が良いDI(ディペンデンシー・インジェクション)について解説していきます。
まずはコードを見た方が早いと思うので、次の2つのコードを見比べてみてください。
// DIがない場合
class PizzaMaker {
private doughProvider: DoughProvider;
private sauceProvider: SauceProvider;
constructor() {
this.doughProvider = new DoughProvider();
this.sauceProvider = new SauceProvider();
}
makePizza() {
const dough = this.doughProvider.getDough();
const sauce = this.sauceProvider.getSauce();
// ピザを作る処理...
}
}
class DoughProvider {
getDough() {
// パン生地を作る処理...
}
}
class SauceProvider {
getSauce() {
// ソースを作る処理...
}
}
// DIがある場合
class PizzaMaker {
constructor(
private doughProvider: DoughProvider,
private sauceProvider: SauceProvider
) {}
makePizza() {
const dough = this.doughProvider.getDough();
const sauce = this.sauceProvider.getSauce();
// ピザを作る処理...
}
}
class DoughProvider {
getDough() {
// パン生地を作る処理...
}
}
class SauceProvider {
getSauce() {
// ソースを作る処理...
}
}
const doughProvider = new DoughProvider();
const sauceProvider = new SauceProvider();
const pizzaMaker = new PizzaMaker(doughProvider, sauceProvider);
DIがない場合は、あるクラスで他のクラスをインスタンス化しています。
逆にDIがあると、PizzaMakerクラス
が実質的に他のクラスに依存しなくなります。
このテクニックを使うことで、次のメリットが得られます。
- 責務がしっかりと分かれる
- テストがしやすくなる
- 依存関係がなくなり、コードの柔軟性が高まる
実際に実務で使わないとなかなか分かりづらいと思うので、こんなテクニックもあるんだなーくらいの理解でOKです。
一応、理解を深めるためにChat GPT君の解説も置いておきます。
まとめ
今回は、Web APIについて包括的な解説をしてきました。
ぜひこの記事を参考にWebAPIへの理解を深めてください。
宣伝
0からエンジニアになるためのノウハウをブログで発信しています。
また、YouTubeでの動画解説も始めました。
インスタの発信も細々とやっています。
興味がある方は、ぜひリンクをクリックして確認してみてください!
Discussion