Flask-RESTXでAPIを作ってあそぶ(基本編)

# はじめに
pythonのフレームワークで最も有名なのはDjangoだと思います。
今回もDjangoのRest FrameWorkである[DRF](https://www.django-rest-framework.org/)の記事を書こうと思っていました。
しかし、調べているうちにとある記事を見つけてしまったのです。
[Pirates use Flask, the Navy uses Django](https://wakatime.com/blog/14-pirates-use-flask-the-navy-uses-django)(海賊はFlaskを使い、海軍はDjangoを使う)
私は決して海軍なんかではありません。すぐにFlaskの学習を始めました。

## Flaskについて
FlaskはpythonのWebアプリケーションフレームワークでDjangoと人気を2分しています。
Djangoとの違いはなんでしょうか？
Djangoは「フルスタックのMTCライブラリ」で独自のORMが特徴です。膨大な数のパッケージが組み込まれており、一般的には大規模なサービスで使われることが多いです。
一方でFlaskは必要最低限の機能を提供する「マイクロフレームワーク」です。1ファイルだけでFlaskアプリケーションが作成できます。SPAで使われることが多いですが、必要に応じてライブラリを追加することで、拡張もできます。

## REST APIを作成する
Flask2系のライブラリはFlask-RESTXしか見つかりませんでした。
このライブラリで遊んでいきましょう。

### インストール
```zsh
mkdir sample
cd sample
python3 -m venv venv
source venv/bin/activate
pip install --upgrade pip
pip install flask-restx
pip freeze

aniso8601==9.0.1
attrs==21.2.0
click==8.0.3
Flask==2.0.2
flask-restx==0.5.1
importlib-resources==5.4.0
itsdangerous==2.0.1
Jinja2==3.0.3
jsonschema==4.3.1
MarkupSafe==2.0.1
pyrsistent==0.18.0
pytz==2021.3
six==1.16.0
Werkzeug==2.0.2
zipp==3.6.0
```
これで準備が完了しました。

### 最小限のAPI
```python:sample_api.py
from flask import Flask
from flask_restx import Resource, Api

app = Flask(__name__)
api = Api(app)

@api.route('/hello')
class HelloWorld(Resource):
    def get(self):
        return {'hello': 'world'}

if __name__ == '__main__':
    app.run(debug=True)
```
ドキュメントによるとこれだけで動くそうです。
まさに海賊ですね。

```zsh
python sample_api.py
curl -i http://127.0.0.1:5000/hello
HTTP/1.0 200 OK
Content-Type: application/json
Content-Length: 25
Server: Werkzeug/2.0.2 Python/3.8.9
Date: Wed, 22 Dec 2021 07:59:33 GMT
{
    "hello": "world"
}
```

### ルーティング
ルーティングはroute()アノテーション、またはadd_resourceメソッドで定義できます
```api.add_resource(User, /user/, '/user/<int:id>')```
```@api.route('/user/','/user/<int:id>') ```

メソッドのディスパッチはもっと簡単です。
HTTPでGETリクエストを送信した場合、GETメソッドが実行されます。

```python:sample_api.py
from flask import Flask
from flask_restx import Resource, Api
import inspect

app = Flask(__name__)
api = Api(app)

@api.route('/user/','/user/<int:id>')
class User(Resource):
    def get(self, id=None):
        if id is None:
            return {inspect.currentframe().f_code.co_name: 'call GET method list user'}
        else:
            return {inspect.currentframe().f_code.co_name: f'call GET method single user {id}'}

    def post(self):
        return {inspect.currentframe().f_code.co_name: 'call POST method'}

    def put (self, id):
        return {inspect.currentframe().f_code.co_name: 'call PUT method'}

    def delete(self, id):
        return {inspect.currentframe().f_code.co_name: 'call DELETE method'}

if __name__ == '__main__':
    app.run(debug=True)
```
```zsh
curl http://127.0.0.1:5000/user/       
{"get": "call GET method list user"}

curl http://127.0.0.1:5000/user/1      
{"get": "call GET method single user 1"}

curl -XPOST http://127.0.0.1:5000/user/
{"post": "call POST method"}

curl -XPUT http://127.0.0.1:5000/user/1
{"put": "call PUT method"}

curl -XDELETE http://127.0.0.1:5000/user/1
{"delete": "call DELETE method"}
```

ステータスコードを変更したい場合はこんな感じです。

```python
    def post(self):
        return {inspect.currentframe().f_code.co_name: 'call POST method'}, 201
```
```zsh
curl -XPOST http://127.0.0.1:5000/user/ -i
HTTP/1.0 201 CREATED
Content-Type: application/json
Content-Length: 35
Server: Werkzeug/2.0.2 Python/3.8.9
Date: Thu, 23 Dec 2021 00:44:55 GMT
{"post": "call POST method"}
```

### モデル
モデルの定義は[fields](https://flask-restx.readthedocs.io/en/latest/api.html#flask_restx.fields.String)を用いて定義します。

```python:example
model = api.model('Model', {
    'name': fields.String,
    'age': fields.Integer
    'birthday': fields.DateTime()
})
```

### シリアライズ
marshal_withデコレータで戻り値にシリアライズを適用することができます。

```python:example
app = Flask(__name__)
api = Api(app)

user_model = api.model('User', {
    'name': fields.String,
    'age': fields.Integer
    'birthday': fields.DateTime()
})

@api.route('/user/<int:id>')
@api.response(404, 'Not found')
class User(Resource):
    @api.marshal_with(user_model)
    def get(self, id):
        return db_function.get(id)
```

### ORM
Flaskは最小限の機能しかないのでORMはありません
ORMで有名なライブラリは[flask-sqlalchemy](https://flask-sqlalchemy.palletsprojects.com/en/2.x/)です。
こちらは次回の応用編(機会があったら)で説明します。

## まとめ
Flask-RESTXの基本編はいかがだったでしょうか。
簡単にAPIが作れることがわかりました。
プロトタイプを作る際などは高速に開発ができるので便利だと思います。

Djangoは海軍でFlaskは海賊
いまだに意味はわかりませんが、そのうち海賊から足を洗い、海軍に戻ります。