flaskを使ってRestAPIサーバを作ってみる

# 0. 始める前に

Webアプリケーションを作りシステム作りが初めてであってもどこからどこまで実施すれば動くシステムができるのかを開発して体験してみます。
前準備として次の記事を見てアプリケーションに必要なツールの導入をしてください。
* [Flask+MySQL on dockerを始める準備](https://qiita.com/kaorunix/items/dacb0122898c18ad6b57)
* [Flask+MySQL on docker](https://qiita.com/kaorunix/items/c04d250bda904e9f07f2)

FlaskはpythonのWebアプリ開発フレームワークです。Webサーバーを立ち上げてURLに応じた画面を表示したり、リクエストに対する処理をpythonで作り処理結果を返すことができます。

### 0.1. DBにテーブルを追加する

本記事はユーザーアカウントを想定したAccountというテーブルを使います。
異なるテーブルを使う場合、[Flask+MySQL on docker](https://qiita.com/kaorunix/items/c04d250bda904e9f07f2)で構築したMySQLにmysqlコマンドでログインして次の様なSQLを実行してください。

```sql
CREATE TABLE `account` (
  `id` int NOT NULL AUTO_INCREMENT,
  `account_name` varchar(64) NOT NULL UNIQUE,
  `start_on` datetime NOT NULL,
  `end_on` datetime NOT NULL,
  `created_by` int DEFAULT NULL,
  `created_at` datetime DEFAULT NULL,
  `updated_by` int DEFAULT NULL,
  `updated_at` datetime DEFAULT NULL,
  `status` int NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
```

このSQLはAccountテーブルを作るものですが[MySQL 5.6 リファレンスマニュアル](https://dev.mysql.com/doc/refman/5.6/ja/create-table.html)などを参考に目的にあったテーブルに項目、データ型は読み替えてください。

### 0.2. flask_sv プロジェクト

[Flask+MySQL on docker](https://qiita.com/kaorunix/items/c04d250bda904e9f07f2)で公開しました flask_sv というプロジェクトを発展させてWebアプリの作り方を確認します。

始める前にのリンクでは構築方法を詳しく書いていますが、以下の手順を実行したことを確認してください。

```
$ git clone git@github.com:kaorunix/flask_sv.git
$ cd flask_sv
```

git cloneの時に次のエラーが出る人はsshの鍵を登録していないのではないでしょうか。

```
Permission denied (publickey,gssapi-keyex,gssapi-with-mic).
fatal: Could not read from remote repository.
```

もし、一時的に回避するのであれば次の様に入力してください。

```
$ git clone https://github.com/kaorunix/flask_sv.git
```

本章で説明するコードは `feature/chapter2` ブランチを対象とします。
**flask_sv** プロジェクトのディレクトリ配下で次のコマンドを実行してください。

```shell
$ git checkout feature/chapter2
```

次にPipenvを起動します。
プロジェクト(flask_sv)ディレクトリ配下で次のコマンドを入力してください。

```
$ pipenv shell
```

次のコマンドで必要なパッケージをインストールします。

```
$ pipenv install
$ pipenv install --dev
```

次のコマンドでMySQL on dockerの環境変数を読み込みます。
必要に応じて *bin/env.sh* の書き換えを行ってください。

```
$ source bin/env.sh
```


### 0.3. flaskの仕組み

なるべく簡単にflaskを使うため、main.pyを解説します。
まず、コード全部を載せます。

```backend/src/main.py
from flask import Flask, render_template
from api import api_bp
app.config['JSON_AS_ASCII'] = False

app = Flask(__name__, static_folder='../../frontend/dist/static', template_folder='../../frontend')
app.register_blueprint(api_bp)

@app.route('/', defaults={'path': ''})
@app.route('/<path:path>')
def index(path):
    return render_template('index.html')

if __name__ == '__main__':
    app.run(debug=True)
```

flaskのライブラリを使うために次のインポート文があります。

```py
from flask import Flask, render_template
```

Blueprintというflaskのモジュールを分割するライブラリでapiはapi.pyに実装しています。そのapi.pyのインポートです。

```py
from api import api_bp
```

Flaskの静的パス指定。Blueprintの登録。

```py
app = Flask(__name__, static_folder='../../frontend/dist/static', template_folder='../../frontend')
app.register_blueprint(api_bp)
```

アプリケーションを作る時に、jsonの中に日本語文字列があるとjson.dumpsでエンコードがASCIIになってしまい文字化けしてしまいます。次の記述でASCII以外のエンコードが使える様になります。

```
app.config['JSON_AS_ASCII'] = False
```

次は、`/` へのリクエストで返却するファイルを指定しています。
render_templateは引数で渡したファイルに値を埋め込み表示内容を作り替えることができますが、ここでは説明しません。

[Flaskで学ぶWebアプリケーションのしくみとつくり方 P.73](https://www.amazon.co.jp/dp/4802612249)などを読むとテンプレートを使ったウェブアプリから説明が始まります。

```py
@app.route('/', defaults={'path': ''})
@app.route('/<path:path>')
def index(path):
    return render_template('index.html')
```

次の表現はコマンドラインから実行されたらapp.runを実行するという意味になります。
おまじないとしてメインプログラムの最後に入れておきます。

```py
if __name__ == '__main__':
    app.run(debug=True)
```

### 0.4. 実行

#### 0.4.1. flask_svの起動

[Flask+MySQL on docker](https://qiita.com/kaorunix/items/c04d250bda904e9f07f2#:~:text=%E3%81%A3%E3%81%A6%E3%81%8F%E3%81%A0%E3%81%95%E3%81%84%E3%80%82-,Project,-%E4%BB%8A%E5%9B%9E%E3%81%AE%E5%8B%89%E5%BC%B7) の前準備が終わっている状態で **flask_sv** のディレクトリで次のコマンドを入力します。

```
$ pipenv run python backend/src/main.py
 * Serving Flask app 'main' (lazy loading)
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: on
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 538-199-926
```
### 0.5. DB接続設定の実装

DBの接続情報は次のファイルに集約しています。

pythonのORMライブラリであるsqlalchemyを使用しています。

sqlalchemyについては[SQLAlchemyの基本的な使い方](https://qiita.com/ariku/items/75799665acd09520bed2)や、[Flaskで学ぶWebアプリケーションのしくみとつくり方 5-2 SQLAlchemyでORMに挑戦！ P.256](https://www.amazon.co.jp/dp/4802612249)などが参考になりました。

次のコードではsqlalchemyを使ってDBの設定情報を記載しているので本プログラムをimportとしてデータベース操作のプログラムを記述します。

```model/db.py
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

import os
import subprocess

DATABASE = 'mysql+pymysql'
USER = 'creist'
PASSWORD = os.environ['MYSQL_PASSWORD']
HOST = '127.0.0.1'
PORT = '3306'
DB_NAME = 'flask_sv'

CONNECT_STR = '{}://{}:{}@{}:{}/{}'.format(DATABASE, USER, PASSWORD, HOST, PORT, DB_NAME)

engine = create_engine(CONNECT_STR)

Base = declarative_base()
```

### 0.6. サンプルデータの登録

次のコマンドでmysqlにログインし、サンプルデータを登録しましょう。

```
$ mysql -h 127.0.0.1 -u creist -p flask_sv
```

```
mysql> insert into account (account_name, start_on, end_on, created_by, created_at, status) values('sample_account','2021-01-01', '2021-12-31', 99, now(),0);
Query OK, 1 row affected (0.02 sec)

mysql>
```

出来上がったらidを確認しましょう。

```
mysql> select * from account;
+-----+-----------------+---------------------+---------------------+------------+---------------------+------------+------------+--------+
| id  | account_name    | start_on            | end_on              | created_by | created_at          | updated_by | updated_at | status |
+-----+-----------------+---------------------+---------------------+------------+---------------------+------------+------------+--------+
| 102 | sample_account  | 2021-01-01 00:00:00 | 2021-12-31 00:00:00 |         99 | 2021-07-04 06:20:24 |       NULL | NULL       |      0 |
+-----+-----------------+---------------------+---------------------+------------+---------------------+------------+------------+--------+
1 rows in set (0.01 sec)

mysql>
```

ここで返ってきたid 102に対してAPIで検索をします。

[http://localhost:5000/api/account/get/102](http://localhost:5000/api/account/get/102)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284697/29c3488b-3477-ba0e-1962-e449cfd43cac.png)

ブラウザで見てもJSON形式の文字列が返ってくるのが分かります。
繋がって表示されますが、見やすくするためにはブラウザの拡張機能(chromeであればJSONView)などをインストールしてください。

また、ブラウザで動かすところまでできていないpythonのプログラムを動かす時はテストコードで動かしましょう。

**flask_sv** のディレクトリで次のコマンドを実行するとmodelのAccount関連のテストが動きます。

```shell
$ pytest -v -s backend/tests/model/test_Account.py 
=================================================== test session starts ====================================================
platform darwin -- Python 3.7.3, pytest-6.2.4, py-1.10.0, pluggy-0.13.1 -- /Users/kaoru/creist/Projects/flask_sv_git/.venv/bin/python
cachedir: .pytest_cache
rootdir: /Users/kaoru/creist/Projects/flask_sv_git
collected 2 items                                                                                                          

backend/tests/model/test_Account.py::test_getById PASSED
backend/tests/model/test_Account.py::test_create PASSED

==================================================== 2 passed in 0.15s =====================================================
$
```

`backend/tests/model/test_Account.py`に記述された`test_getById`関数、`test_create`関数の試験が成功したというメッセージです。

作ったプログラムはテストコードで動作を確認しましょう。
テストコードはtests配下に正規のパッケージ(srcディレクトリ配下)と同じディレクトリ構成で作成します。

次のファイルは特殊でテストで共通利用するコードを記述します。
まずここでは、`../src`配下の`__init__.py`を検索し、パッケージディレクトリとしての検索パスとします。これがテストコードと正規のパッケージと同じパッケージ名で使える様にする秘密です。

```tests/conftest.py
import sys 
import os
sys.path.append(os.path.abspath(os.path.dirname(os.path.abspath(__file__)) + "/../src/"))
...
```


この後は、それぞれの基本機能の実装を見ていきます。
qiitaの別記事として書きましたので、それぞれのリンクを参照してください。

※ ブラウザでアカウントレコードを検索した時に、次のエラーになることがある様です。

```
cryptography is required for sha256_password or caching_sha2_password
```

その時はmysqlの再起動、flaskの再起動などで直ることもありますがそれでも直らない時はflask_svのフォルダ内で次のコマンドを実行してください。

```
$ pipenv install cryptography
```


# 1. 参照 [flaskを使ってRestAPIサーバを作ってみる(参照編)](https://qiita.com/kaorunix/items/05f47b937d83e2324c4d)

# 2. 作成 [flaskを使ってRestAPIサーバを作ってみる(作成編)](https://qiita.com/kaorunix/items/00eae1270265629d7033)

# 3. 検索 [flaskを使ってRestAPIサーバを作ってみる(検索編)](https://qiita.com/kaorunix/items/d510d70e47e4ee27cdc3)

# 4. 編集 [flaskを使ってRestAPIサーバを作ってみる(編集編)](https://qiita.com/kaorunix/items/51e1d223d8c6a8d28bf6)

# 5. 削除 [flaskを使ってRestAPIサーバを作ってみる(削除編)](https://qiita.com/kaorunix/items/2ac5605a3cf5b3b9439a)

# 6. インターネットから接続APIサーバーをデプロイする

### 本番環境に必要なパッケージ

本番環境に必要なパッケージをインストールします。ここは開発PCで作業してください。

pythonでのWEBアプリケーションを作る時には[Web Server Gateway Interface](https://ja.wikipedia.org/wiki/Web_Server_Gateway_Interface)
ここではgunicornというWSGIを使ってアプリケーションサーバーを動かします。

gunicornを使うためインストールをします。

```
$ pipenv install gunicorn 
```

### 本番環境のEC2を作る

### ALB(アプリケーション・ロード・バランサー)を作る

お試しなのでロードバランシング必要なほどのリクエストは来ないのですが、httpのセキュリティが厳しくなり、暗号化されたhttpsでないと外部のアプリケーションを呼び出すことができません。ブラウザでエラーとなってしまいます。

ALBのオプションとしてsslの証明書を発行するサービスのAWS Certificate Managerで作った証明書を利用することができます。
AWS Certificate Managerは自分が管理するドメインの証明書を作成することができます。管理するドメインとはWHOISの管理者メールアドレスを受け取れるということです。