【入門】Flask + Python で REST API を設計・実装

スポンサーリンク

REST API (Representational State Transfer API) とは

REST API (Representational State Transfer API) とは、以下の6つの REST アーキテクチャの制約に準拠する API のことです。

  • Client-Server (クライアントとサーバーの分離)
  • Stateless (ステートレス性)
  • Cache (キャッシュ可能性)
  • Uniform Interface (統一されたインターフェース)
  • Layered System (階層化されたシステム・アーキテクチャー)
  • Code-On-Demand (コードオンデマンド) [オプション]

引用元が O'Reilly であるものや、企業のドキュメントを重視しました。

https://en.wikipedia.org/wiki/Representational_state_transfer
https://www.redhat.com/ja/topics/api/what-is-a-rest-api
https://www.ibm.com/jp-ja/cloud/learn/rest-apis
https://docs.oracle.com/cd/E28613_01/web.1211/b65960/overview.htm

上の説明だけ読んでもよくわからないと思うので、実例を見ていきましょう。

HTTP ベースの REST API の例でわかりやすく説明

HTTP ベースの REST API では、次の2つの条件を満たす必要があります。(正確にはこちら)

  • URI ベースのエンドポイントで「リソース」を決める (https://api.twitter.com/)
  • HTTP メソッドで、リソースの「操作」を決める (GET, POST, PUT, DELETE)

この条件により、REST アーキテクチャの6つの制約を満たします。

REST アーキテクチャの制約HTTP ベースの REST API が持つ性質
Client-Server (クライアントとサーバーの分離)HTTP プロトコルはサーバークライアントモデル
Stateless (ステートレス性)HTTP プロトコルはステートレス
Cache (キャッシュ可能性)HTTP ヘッダーでキャッシュの指定可能
Uniform Interface (統一されたインターフェース)※1 URI を利用してリソースを識別
※1 HTTP メソッドを利用してリソースの操作を表現
Layered System (階層化されたシステム・アーキテクチャー)HTTP プロトコルは階層化可能。以下例
ロードバランサー > Web サーバー > AP サーバー
Code-On-Demand (コードオンデマンド) [オプション]オプション

※1

Identification of resources - You use the URI (IRI) standard to identify a resource. In this case, a resource is a web document.

Manipulation of resources through these representations - You use the HTTP standard to describe communication. So for example GET means that you want to retrieve data about the URI-identified resource. You can describe an operation with an HTTP method and a URI.

https://stackoverflow.com/questions/25172600/rest-what-exactly-is-meant-by-uniform-interface

例:Twitter の REST API の仕様

Twitter 社が公開している REST API を例として、HTTP ベースの REST API の2つの条件をどのように満たしているか確認します。

URI ベースのエンドポイントで「リソース」を決める

次のように「URI ベースのエンドポイント」と「リソース」が対応しています。

Twitter の URI ベースのエンドポイント対応するリソース
https://api.twitter.com/2/tweets/:idツイート
https://api.twitter.com/2/users/:id/followingフォローしているユーザー
https://api.twitter.com/2/users/:id/likesお気に入り(ふぁぼ)したツイート

HTTP メソッドで、リソースの「操作」を決める

REST API では次のように、「HTTP メソッド (操作)」と「URI (リソース)」を組み合わせることで、リソースを操作します。

HTTP
メソッド
HTTP メソッドの操作内容HTTP メソッドと URI エンドポイントの組
GETリソースを取得GET https://api.twitter.com/2/tweets/:id
ツイートの取得
POSTリソースを追加
(冪等性無し)
POST https://api.twitter.com/2/tweets
ツイートの投稿
PUTリソースの作成 or 置き換え
(冪等性あり)
ツイートは置き換え (上書き) できない
= 該当する Twitter の API は無い
DELETEリソースを削除DELETE https://api.twitter.com/2/tweets/:id
ツイートを削除
スポンサーリンク

REST API のサンプル

ここでは、Python + Flask を利用して実際に REST API サーバーを作成します。

Flask は Web アプリケーションフレームワークです。Flask の詳細については以下の記事をご覧ください。

また、より実践的な REST API の設計・実装する場合は以下の書籍をご覧ください。

RESTFulなアーキテクチャの概念や実際に Web サービスを設計する方法

Web APIの設計、開発、運用について

作成する REST API の設計

ここでは、以下のような Twitter のツイート機能を持つ REST API を作成します。

REST APIREST API の機能
GET /tweetツイートを取得
POST /tweetツイートを投稿
PUT /tweet/:id指定した ID のツイートを更新
DELETE /tweet/:id指定した ID のツイートを削除

REST API サーバーを作成

Python + Flask を利用して REST API サーバーを作成します。

なお、リソースの保存先には SQLite を利用しています。SQLite の詳細は以下の記事をご覧ください。

pip3 install flask
vim app.py
from flask import Flask, g, request                                                                                               
import sqlite3
import json   
              
dbpath = 'test.db' #テーブルを保存するファイル
app = Flask(__name__)
              
def get_db():#データベースのコネクションを取得
    db = getattr(g, '_database', None)
    if db is None:
        db = g._database = sqlite3.connect(dbpath)
        db.execute('CREATE TABLE IF NOT EXISTS tweets_tbl(id INTEGER PRIMARY KEY AUTOINCREMENT, tweet VARCHAR(140))')
    return db 
              
@app.route('/tweet', methods=['GET'])
def get_tweet():
    con = get_db() #コネクションを取得
    con.row_factory = sqlite3.Row #カラム名取得のため
    cur = con.cursor() #カーソル取得
              
    cur.execute('SELECT * FROM tweets_tbl')
    tweets = []
    for row in cur.fetchall(): #sqlite3.Row オブジェクトを dict に変換
        tweets.append(dict(row))
              
    return json.dumps(tweets,indent=2)
              
@app.route('/tweet', methods=['POST'])
def post_tweet():
    con = get_db() #コネクション
    cur = con.cursor() #カーソルインスタンスを作成
              
    tweet = request.json["tweet"] #POSTメソッド のデータを取得
    cur.execute(f"INSERT INTO tweets_tbl(tweet) values('{tweet}')") #ツイート文をテーブルにINSERT
    con.commit()
              
    return 'Success a Tweet!\n'
              
@app.route('/tweet/<id>', methods=['PUT'])
def put_tweet(id):
    con = get_db() #コネクション
    cur = con.cursor() #カーソルインスタンスを作成
              
    tweet = request.json["tweet"]
    cur.execute(f"UPDATE tweets_tbl SET tweet = '{tweet}' WHERE id = {id}")
    con.commit()
              
    return 'Update a Tweet!\n'
              
@app.route('/tweet/<id>', methods=['DELETE'])
def delete_tweet(id):
    con = get_db() #コネクション
    cur = con.cursor() #カーソルインスタンスを作成
    cur.execute(f"DELETE FROM tweets_tbl WHERE id = {id}")
    con.commit()
              
    return 'Delete a Tweet!\n'
              
if __name__ == "__main__":
    app.run() 
flask run

REST API クライアントの使い方

今回クライアントとして、以下の2種類を紹介します。

curl をクライアントに利用

ここでは curl を利用して API リクエストを行います。

POST メソッドでツイートを投稿
curl localhost:5000/tweet -XPOST -H "Content-Type: application/json" -d '
{
  "tweet": "Hello World!"
}'
Success a Tweet!
GET メソッドでツイートを取得
curl localhost:5000/tweet -XGET
[
  {
    "id": 1,
    "tweet": "Hello World!"
  }
]
PUT メソッドでツイートを更新
curl localhost:5000/tweet/1 -XPUT -H "Content-Type: application/json" -d '
{
  "tweet":"Change World"
}'
Update a Tweet!
curl localhost:5000/tweet -XGET
[
  {
    "id": 1,
    "tweet": "Change World"
  }
]
DELETE メソッドでツイートを削除
curl localhost:5000/tweet/1 -XDELETE
Delete a Tweet!
curl localhost:5000/tweet -XGET
[]

Python の requests をクライアントに利用

ここでは、Python の requests ライブラリを利用して API リクエストを行います。

POST メソッドでツイートを投稿
vim post_api.py
import requests
import json
 
url = 'http://localhost:5000/tweet'
headers = {'Content-Type': 'application/json'}
data = {"tweet": "Hello World!"}

res = requests.post(url, headers=headers ,data=json.dumps(data))
print(res.text)
python3 post_api.py
Success a Tweet!
GET メソッドでツイートを取得
vim get_api.py
import requests

url = 'http://localhost:5000/tweet'
res = requests.get(url)
print(res.text)
python3 get_api.py
[
  {
    "id": 1,
    "tweet": "Hello World!"
  }
]
PUT メソッドでツイートを更新
vim put_api.py
import requests
import json
 
url = 'http://localhost:5000/tweet/1'
headers = {'Content-Type': 'application/json'}
data = {"tweet": "Change World!"}                                                
 
res = requests.put(url, headers=headers ,data=json.dumps(data))
print(res.text)
python3 put_api.py
Update a Tweet!
python3 get_api.py
[
  {
    "id": 1,
    "tweet": "Change World!"
  }
]
DELETE メソッドでツイートを削除
vim delete_api.py
import requests
import json
 
url = 'http://localhost:5000/tweet/1'
 
res = requests.delete(url)
print(res.text)
python3 delete_api.py
Delete a Tweet!
python3 get_api.py
[]

REST API の認可・認証

REST APIOpenID Authentication 2.0 (OAuth 2.0) や OpenID Connect (OIDC) を利用して認可認証することで、API を叩くユーザーにアクセス制限をします。

OpenID Authentication 2.0 (OAuth 2.0)

アクセストークン (認可情報) により REST API のアクセス権限を制御する場合は、以下の OAuth 2.0 の記事をご覧ください。

OpenID Connect (OIDC)

アクセストークン (認可情報) ・ID トークン (認証情報) により REST API のアクセス権限を制御する場合は、以下の OIDC の記事をご覧ください。

ID フェデレーション

別の認証基盤から ID トークン (認証情報) を受け取る場合は ID フェデレーションの記事をご覧ください。