【徹底解説】REST VS GraphQL
はじめに
今回の記事では、Web APIの開発に重宝されるRESTとGraphQLの違いを解説する。
対象とする読者
- これからREST、またはGraphQLを実務で積極的に活用したいひと
- 両者の違いがわからないひと
- 個人開発等でWeb APIをつくるひと
- タイトルを見てなんとなく気になったひと
APIとは
RESTとGraphQLの議論に入る前に、まずはAPIについて説明する必要がある。
Wikipediaによると、API(Application Programming Interface)は以下のように定義されている。
APIは各種システム/サービスがそのシステム/サービスを利用するアプリケーションに対して公開するインタフェースである。APIの重要な役割は、システム/サービス提供者が公式に仕様(外部仕様)を定義し、管理している各種機能を利用するための操作方法(インタフェース)を提供することである。
要は、APIはアプリケーション間で情報をやり取りするためのインターフェイスである。APIを使うことで、あるシステムが他のシステムの機能やデータに直接アクセスできるようになるのだ。
APIが果たす役割は以下の2つである。
- システム間のデータ共有:Web APIを通じて、異なるシステム間でデータを共有することが可能です。これにより、複数のシステムが連携して動作することができます。
- フロントエンドとバックエンドの分離:Web APIを経由して、フロントエンドとバックエンドを通信できる。そのため、フロントエンドとバックエンドをそれぞれ独立して開発・運用できる。
REST APIとは
REST(REpresentational State Transfer) APIは、Web上のシステム間で情報をやり取りするための一般的な設計パラダイムの一つである。RESTは、リソースを識別してそのリソースの状態をHTTPメソッドで操作するための設計減速を定義する。
REST APIの特徴は以下の通り。
- リソース指向
- HTTPメソッド
- ステートレス
- レスポンスをJSON形式で出力
それぞれ順番に解説する。
リソース指向
REST APIはリソース指向だ。リソース(Resource)とは何らかの情報の塊で、この情報はURL(Uniform Resource Locator)で識別される。例えば、あるユーザを意味するリソースのURLはhttps://api.example.com/users/123
となる。
HTTPメソッド
REST APIでは、以下のようなHTTPメソッドを使ってリソースを操作する。
HTTPメソッド | 意味 |
---|---|
GET |
情報を取得する |
POST |
情報を送信する |
PUT |
情報を上書きする |
DELETE |
情報を削除する |
ステートレス
REST APIはステートレスである。これは、それぞれのリクエストがそれぞれ独立していて、前のリクエストの状態を引き継がないということになる。これにより、サーバの設計がシンプルになって拡張性が向上する。
レスポンスをJSON形式で出力
REST APIのレスポンスは、原則JSONあるいはXML形式で出力される。それ故に、様々なプログラミング言語から簡単に利用できる。
例えば、REST APIでユーザ情報を取得するJavaScriptのコードは以下のようになる。
const fetch = require('node-fetch');
fetch('https://api.example.com/users/123')
.then(response => response.json())
.then(user => console.log(user))
.catch(err => console.error(err));
上述のコードは、https://api.example.com/users/123
のリソースをGETメソッドで取得し、その結果をJSON形式でパースする。
このように、REST APIはWeb上のシステム間で情報をやり取りするためのシンプルで一般的な方法を提供する。
REST APIの長所
単純明快
REST APIのリソースはURLで一意に識別される。例えば、特定のユーザの情報を取得するには以下のようなリクエストを行う。
fetch('https://example.com/users/123')
.then(response => response.json())
.then(data => console.log(data));
HTTPプロトコルとの親和性
REST APIはHTTPメソッドを用いてリソースを操作する。ユーザ情報の更新はPUT
メソッドで実行できる。
fetch('https://example.com/users/123', {
method: 'PUT',
body: JSON.stringify({ name: 'New Monster Name' })
})
.then(response => response.json())
.then(data => console.log(data));
REST APIの短所
データを過剰に取得する
REST APIでは、特定のユーザの名前だけが欲しい場合でも、すべての情報が取得される。また、ユーザとその住所に関する情報を一度に取得するには2つの異なるリクエストをしなければならない。
fetch('https://example.com/users/456')
.then(response => response.json())
.then(data => console.log(data));
fetch('https://example.com/users/456/address')
.then(response => response.json())
.then(data => console.log(data));
バージョン管理が必須
REST APIを変更する際には新しいバージョン、あるいは新しいエンドポイントを作らなければならない。
fetch('https://example.com/v2/users/123')
.then(response => response.json())
.then(data => console.log(data));
GraphQL APIとは
GraphQLはAPIを開発するためのクエリ言語だ。2015年にFacebook(現在のMeta社)が、RESTの問題を解決するために開発した。RESTとの最大の違いは、クライアントが必要なデータの構造を任意で定義できる点にある。(詳細は後述)
REST APIでは、HTTPメソッドに従ってCRUDを軸にデータの取得、編集や削除を行う一方で、GraphQLではデータの問い合わせ(Query)、書き換え(Mutation)をサポートする。
GraphQLの概念とRESTのCRUDをそれぞれ照らし合わせると以下のようになる。
- Query:Read(取得)
- Mutation:Create(作成)、Update(更新)、Delete(削除)
GraphQLでは、クライアントがクエリ内容を記述したdocumentを送信し、GraphQLサービスがクエリを実行して結果を出力する。documentはGraphQL独自の言語(これをGraphQL query languageという)で書く。
GraphQLの長所
データを効率的に取得できる
GraphQL APIでは、クライアントが必要なデータだけを具体的に要求できる。
fetch('https://example.com/graphql', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' },
body: JSON.stringify({
query: `
{
user(id: 456) {
name
address {
country
city
}
}
}
`
})
})
.then(response => response.json())
.then(data => console.log(data));
バージョニングが不要
GraphQL APIはREST APIのようなバージョン管理が不要である。APIの仕様を変更したい場合は新しいフィールドを追加するだけでいい。
GraphQLの短所
複雑さ
GraphQLはクライアントが取得するデータを任意に定義できるので柔軟性と豊かな表現が得意だ。そのため、APIが複雑になってしまいシンプルなAPIとの相性が最悪な可能性がある。
HTTPとの相性が悪い
GraphQLはRESTと違って、独自のアプローチでデータを取得するためHTTPとの相性が悪い。
例えば、REST APIにはCRUDと言って、データの作成や上書き、削除などをHTTPメソッドに対応させて設計できる。ところが、GraphQL APIにおけるデータの操作はQuery(データの読み込み)とMutation(データの編集)という2種類しかない。そのため、HTTPメソッドに対応させるのが難しいのだ。
▼ 表1:REST APIの場合(CRUD)
CRUD | 対応するHTTPメソッド |
---|---|
Create(新規作成) | POST |
Read(読み込み) | GET |
Update(上書き) | PUT |
Delete(削除) | DELETE |
▼ 表2:GraphQL APIの場合
名前 | 対応するHTTPメソッド |
---|---|
Query(データの読み込み) | GET |
Mutation(データの編集) |
POST 、PUT 、DELETE
|
上述の表を見てもらうとわかるが、GraphQLのMutationはCRUDにおけるRead(読み込み)以外の機能を全部担当するような形になっている。
両者の違い
前のパートでは、RESTとGraphQLのそれぞれの長所や短所を解説した。しかし、これだけを説明しても具体的にどう違うのかを理解するのは難しいかもしれない。
ここで、REST APIとGraphQL APIの違いを、『ドラゴンクエスト』を具体例に解説する。例えば、「特定のモンスターのデータを取得する」ケースを考えてみよう。
データ取得
例えば、『ドラゴンクエスト』に登場する特定のモンスターのデータを取得する場合を考えてみよう。
REST APIの場合、リソースはURLで定義される。特定のモンスターについての情報を取得するには、そのモンスターのIDをURLに含めた`GETリクエストを送信することになる。
fetch('https://example.com/monsters/123')
.then(response => response.json())
.then(data => console.log(data));
ここで、他のキャラクターのデータを取得したい場合を考えると、別途そのキャラクターのIDをURLに含めたリクエストを送信する必要がある。
fetch('https://example.com/characters/456')
.then(response => response.json())
.then(data => console.log(data));
一方で、GraphQLの場合はクライアントがサーバに対して必要なデータを具体的に要求する。上述の情報を一度のリクエストで取得できる。
fetch('https://exmaple.com/graphql', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
body: JSON.stringify({
query: `
{
monster(id: 123) {
name
attack
defense
}
charater(id: 456) {
name
level
equipment {
name
power
}
}
}
`
})
})
.then(response => response.json())
.then(data => console.log(data));
このように、GraphQLは単一のリクエストで複数のリソースを取得できる。さらに、「特定のモンスターのHPだけを取得したい」場合でも、以下のようにピンポイントで情報を取得できる。
fetch('https://exmaple.com/graphql', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
body: JSON.stringify({
query: `
{
monster(id: 123) {
hp
}
}
`
})
})
.then(response => response.json())
.then(data => console.log(data));
クエリの柔軟性
RESTとGraphQLでは、「クエリの柔軟性」という観点でも大きな違いがある。
まず、RESTの例を見てみよう。例えば、「特定のモンスターの情報と、モンスターがドロップするアイテムの情報を取得する」場合を考える。これをRESTで表現するには以下のように、2つのリクエストを送信することになるだろう。
// NOTE: Node.jsで実行する場合
// フェッチライブラリを使用してHTTPリクエストを行う
const fetch = require('node-fetch');
// モンスターの情報を取得
fetch('https://example.com/monsters/1')
.then(response => response.json())
.then(warrior => console.log(warrior))
.catch(err => console.error(err));
// モンスターがドロップするアイテムの情報を取得
fetch('https://example.com/warriors/1/items')
.then(response => response.json())
.then(weapons => console.log(weapons))
.catch(err => console.error(err));
上述のコードは、2つのHTTPリクエストを非同期的に行い、それぞれのレスポンスを処理している。
一方で、GraphQLではこれらの情報を一度のリクエストで処理できる。
// NOTE: Node.jsで実行する場合
// GraphQL: Apolloクライアントを使用してGraphQLクエリを送信する
const { ApolloClient, InMemoryCache, gql } = require('@apollo/client');
const client = new ApolloClient({
uri: 'https://api.example.com/graphql',
cache: new InMemoryCache()
});
// モンスターとドロップするアイテムを同時に取得
client.query({
query: gql`
{
monster(id: 1) {
name
level
items {
name
effect
}
}
}
`
})
.then(data => console.log(data))
.catch(err => console.error(err));
上述のGraphQLのクエリは、モンスターの名前とレベル、そしてモンスターがドロップするアイテムを一回のリクエストで取得している。上述の例からわかるように、GraphQLはRESTとは違って一回のリクエストで複数のリソースを取得できるのだ。
エラーハンドリング
RESTとGraphQLではエラーハンドリングの方法にも違いがある。
RESTの場合、HTTPステータスコードでエラーの種類を示す。
fetch('https://example.com/monsters/9999')
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
})
.then(warrior => console.log(warrior))
.catch(err => console.error(err));
上述のコードでは、もしHTTPステータスコードが200以外であればエラーを出力する。
一方で、GraphQLでは常にHTTPステータスコード200を返す。かわりに、エラーの情報はレスポンスのerror
フィールドに含まれる。
// NOTE: Node.jsで動かす場合
const { ApolloClient, InMemoryCache, gql } = require('@apollo/client');
const client = new ApolloClient({
uri: 'https://api.example.com/graphql',
cache: new InMemoryCache()
});
client.query({
query: gql`
{
warrior(id: 9999) {
name
}
}
`
})
.then(response => {
if (response.errors) {
response.errors.forEach(error => console.error(error.message));
} else {
console.log(response.data);
}
})
.catch(err => console.error(err));
上述のコードでは、レスポンスにerror
フィールドが存在するかどうかを確認している。もしエラーがあればそれぞれのエラーメッセージを出力する。エラーがなければデータを表示する。
このように、RESTとGraphQLではエラーハンドリングの方法に大きな差がある。これらの違いを理解することはエラーを適切に対応する上で重要だ。
RESTとGraphQL、どちらを選ぶべきか
ここまでRESTとGraphQL両方の長所や短所、具体的な違いをコードベースで解説した。RESTとGraphQLはどちらを選ぶべきか迷っているひとも少なくないだろうか。
これに対する私の回答は、「両方とも学ぶのが理想だが、RESTは必ず学ぶべき」である。理由は、多くの一般的なWebサービス、特にTwitter等の大規模なWebサービスでは以下のような理由でREST APIが活用されているからだ。
-
HTTPとの相性の良さ:RESTはHTTPメソッド(
GET
、POST
、PUT
、DELETE
等)を直接利用する。故に、HTTPの特徴を最大限に活かせる。 - シンプルさ:RESTはシンプルなデータモデルとアーキテクチャを持つ。それぞれのリソースはURLで一意に識別され、HTTPメソッドを用いて操作される。これによって、APIの設計や開発が簡単になる。
- 成熟したエコシステム:REST APIを設計、開発、テスト、管理するためのツールやライブラリ、ベストプラクティスは数多く存在する。これは、開発の生産性に大きな影響を与えている。
以上の点を踏まえると、これからAPI設計・開発で生計を立てたいなら最優先でRESTを学ぶべきだと断言できる。
GraphQLを学ぶべき理由
前のパートではRESTを最優先して学ぶべき理由を詳細に解説した。今度はGraphQLを学ぶべき理由を解説する。
- 取得できるデータを自由自在に調整できる:GraphQLはクライアントが必要なデータだけを要求できるように設計されている。RESTでは、キャラクターと装備している武器の情報をそれぞれ別でリクエストをサーバに送信する必要がある。
- プロトタイピングを迅速にできる:GraphQLは複数のリソースにアクセスできる単一のエンドポイントを公開する。例えば、アプリのUIが変更されてより多くのデータあるいはより少ないデータが必要になったとしても、サーバやAPIの構造への影響は少ない。
- バージョニングが不要:REST APIでは、データの構造が変わると新しいバージョンを作るか、あるいは新しいエンドポイントを作るかのどちらかになる。ところが、GraphQLでは新しくフィールドを追加することで、既存のクライアントに影響を与えることなく新しいデータ要件に対応できる。
- API設計・開発の選択肢を広げる:RESTとは違うアプローチでAPIを開発する技術を学ぶことは、API設計・開発の選択肢を広げることにつながる。個人的に最も主張したい理由だ。
おわりに
今回はWeb APIの開発で重宝されるRESTとGraphQL、両者の違いをコードベースで徹底解説した。両者の違いを簡潔に表でまとめると以下のようになる。
項目 | REST | GraphQL |
---|---|---|
データ取得 | URLに接続 | 1回のリクエストで複数のリソースからデータを要求できる |
構造 | シンプル | 複雑 |
データの取り扱いかた | CRUD | Query、Mutation |
最大の長所 | シンプルさ、HTTPとの親和性 | 取得できるデータを任意で定義できる |
最大の短所 | データを過剰に取得する | HTTPとの相性が悪い |
技術の取得 | HTTPとの相性が抜群で、クライアントとサーバの理解と密接に関連しているので比較的簡単 | 独特の概念が登場するのでRESTより難易度が高め |
データ形式 | JSON、XML | JSON |
今回の記事が、あなたの今後のWeb API設計・開発の技術選定で参考になれば幸いである。
参考サイト
Discussion