Flask REST API サンプル

## はじめに

FlaskでREST APIの作成を行う際に、記述方法等を他のソースを見たり、Googleで検索したりと、毎回同じことをやっているので、よく使うFlaskの記述等をまとめたサンプルを作成しました。

ソースに色々コメントを書いているので、参考になればいいと思っています。

[ソース:github](https://github.com/tchnkmr/flask_rest_api_sample.git)


### 構成

Flask、Flask_SQLAlchemyを使用したREST APIになります。

### API一覧

|処理|メソッド|エンドポイント|
|:---|:------|:------------|
|ユーザ一覧取得|GET|/api/users|
|ユーザ登録|POST|/api/users|
|ユーザ取得|GET|/api/users/:id|
|ユーザ更新|PUT|/api/users/:id|
|ユーザ削除|DELETE|/api/users/:id|


### サンプルソース

REST APIの部分になります。他の箇所はgithubを参照してください。

```python3:api.py
from flask import Blueprint, request, abort, jsonify

from models import db, User

# api Blueprint作成　http://host/api 以下のものはここのルールで処理される
api = Blueprint('api', __name__, url_prefix='/api')


# エンドポイント http:/host/api/users, GETメソッドのみ受け付ける
# routeは複数指定も可能、methodsはリストなので複数指定可能
@api.route('/users', methods=['GET'])
def list_user():
    # クエリーパラメータ取得 request.args.get
    # 第一引数:パラメータ名、default=で初期値、type=で変換する型を指定できる
    q_limit = request.args.get('limit', default=-1, type=int)
    q_offset = request.args.get('offset', default=0, type=int)

    if q_limit == -1:
        # DBから全件取得
        users = User.query.all()
    else:
        # DBからoffset, limitを使用して取得
        users = User.query.offset(q_offset).limit(q_limit)

    # jsonレスポンス返却
    # jsonifyにdict型オブジェクトを設定するとjsonデータのレスポンスが生成される
    return jsonify({'users': [user.to_dict() for user in users]})


# URL中のパラメータ取得 <type:variable_name>
# type: string(default), int, float, path, uuid
# variable_name: 変数名
@api.route('/users/<int:user_id>', methods=['GET'])
def get_user(user_id=None):
    # DBからフィルタリングして取得
    user = User.query.filter_by(id=user_id).first()
    return jsonify(user.to_dict())


@api.route('/users', methods=['POST'])
def post_user():
    # jsonリクエストから値取得
    payload = request.json
    name = payload.get('name')
    age = payload.get('age')

    # レコードの登録 新規作成したオブジェクトをaddしてcommit
    user = User(name, age)
    db.session.add(user)
    db.session.commit()

    response = jsonify(user.to_dict())
    # レスポンスヘッダ設定
    response.headers['Location'] = '/api/users/%d' % user.id
    # HTTPステータスを200以外で返却したい場合
    return response, 201


@api.route('/users/<user_id>', methods=['PUT'])
def put_user(user_id):
    user = User.query.filter_by(id=user_id).first()
    if not user:
        # エラーハンドラーに処理を移す場合
        # ステータスコード、dict型にてメッセージ等を設定できる
        abort(404, {'code': 'Not found', 'message': 'user not found'})

    # レコードの更新 オブジェクトの値を更新してcommit
    payload = request.json
    user.name = payload.get('name')
    user.age = payload.get('age')
    db.session.commit()

    return jsonify(user.to_dict())


@api.route('/users/<user_id>', methods=['DELETE'])
def delete_user(user_id):
    user = User.query.filter_by(id=user_id).first()
    if not user:
        abort(404, {'code': 'Not found', 'message': 'user not found'})

    # レコードの削除 deleteしてcommit
    db.session.delete(user)
    db.session.commit()

    return jsonify(None), 204


# エラーのハンドリング errorhandler(xxx)を指定、複数指定可能
# ここでは400,404をハンドリングする
@api.errorhandler(400)
@api.errorhandler(404)
def error_handler(error):
    # error.code: HTTPステータスコード
    # error.description: abortで設定したdict型
    return jsonify({'error': {
        'code': error.description['code'],
        'message': error.description['message']
    }}), error.code

```