【初めてのAPI】短縮URLを発行するAPIの開発【Flask + SQLite】

# 概要
短縮URLを発行するAPIサーバを、FlaskとSQLiteを使って実装しました。ハッシュ値の生成には、さまざまな言語に使えるライブラリ「[Hashids](https://hashids.org/)」を使っています。

今回が初めてのAPI開発だったので、この記事では私と同じように初学者の方に向けて、実際にやってみたAPI開発の手順を中心に紹介します。

コードは[GitHub](https://github.com/lily-engine/short_URL)にアップしました。そしてこのバージョンでは、**短縮URLを発行できる外部サービスやAPIを使用していません**。

## 「APIサーバ」とは？
初学者にとって重要なのは、概念を理解することだとつくづく感じています。例えば「APIサーバ」を開発してください、と言われたのに、勘違いして「APIサービス」を開発してしまうと後から慌てて修正する必要があるわけです（実体験です）。

というわけで、今回のお題である「APIサーバ」とは何なのかを理解してみましょう。

まず「API」とは**A**pplication **P**rograming **I**nterfaceの略で、プログラムから使用することを前提としたWebサービスとの窓口を指します。

役割としては、サービスを提供しているWebサーバーに対して、WebAPIが情報の受け渡しをします。このようなAPIを提供元がWeb上に公開しているため、外部のWebサービスからAPIを呼び出して利用可能です。

『[イラスト図解式 この一冊で全部わかるWeb技術の基本](https://www.amazon.co.jp/dp/4797388811/)』の例だと、Yahoo!のようなポータルサイトで天気予報を表示するために、天気予報サイトの外部APIを使用する例が紹介されています。この本を参照したブログ「[REST APIとは？ざっくりと理解してみる【初心者向け】](https://tech.012grp.co.jp/entry/rest_api_basics)」から、以下の図を引用しました。

![スクリーンショット 2022-07-25 15.27.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2523354/f38327f7-ce87-1a8d-c54f-3ec4479b6577.png)
中央の「ポータルサイト」がYahoo!だとすると、ユーザーはYahoo!のサーバーに、住んでいる地域を登録しています。このユーザー情報を、Yahoo!のサーバーから外部APIである天気予報サイトのサーバーに渡すことで、ユーザーはYahoo!で天気予報を見られる仕組みです。

スマホアプリも外部APIと情報の受け渡しをするので、ユーザーが移動しても最新の天気予報が見られます。

つまり運用を想定するのであれば、さまざまなWebサーバが使いやすいようなデータの受け渡しを検討する必要があるわけです（今回のコードではそこまで考えられていませんが、理想として）。

### 「APIサービス」をつくりたい場合

自分なりに「API」について調べても「APIサーバ」を理解できていなかったようで、開発当初は [この記事](https://ja.getdocs.org/how-to-make-a-url-shortener-with-flask-and-sqlite)のコードをそのまま活用したところ、これは「APIサーバ」ではなく「APIサービス」だと指摘をいただきました。

おそらく、本来APIサーバとしてバックエンドで情報を受け渡しすべきところ、フロントエンドに短縮URLを渡しているからだと思われます（つまり「APIを使ったサービス」を開発している、ということかと）。

これだと短縮URLを発行するWebサービスなので、上記のYahoo!と天気予報の例のように、外部のサイト（Yahoo!）と情報を入出力できず、「APIサーバ」として機能していない、ということになります。

というわけで以下では修正後の「APIサーバ」の開発手順を説明しますが、FlaskとSQLiteを使って短縮URLを発行する「APIサービス」をつくりたい場合、私が試したときは以下のブログのコードをそのまま活用できました。コードの解説がわかりやすいです。
 - [FLASKとSQLITEでURL短縮サービスを作成する方法](https://ja.getdocs.org/how-to-make-a-url-shortener-with-flask-and-sqlite)
# APIサーバの開発手順
APIの開発手順として、以下の動画を参照しました。コードはJavaScriptで書かれています。

 - [小学生でもわかるWebAPI入門。ゼロからWebAPIを作ってみよう](https://amateur-engineer-blog.com/flask-api/)

動画で紹介されているWebAPIの作り方は、以下の5ステップです。
1. **画面遷移図を作る**
2. **APIの機能一覧を列挙する**
3. **URLとHTTPメソッドを設定する**
4. **リクエストパラメータを設計する**
5. **レスポンスデータを設計する**

この記事でも、このとおりに進めていきます。
## 画面遷移図を作る
![スクリーンショット 2022-07-25 16.04.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2523354/18b989b4-bdf6-2843-4ade-da81d4f01643.png)

今回のAPIの画面遷移図はとてもシンプルですが、ここで重要なのは、先ほどの動画いわく「**利用イメージを決めること**」。

APIとして外部サービスのサーバと情報を受け渡しするイメージを想像することで、抜け漏れなく開発を進められます。

## APIの機能一覧を列挙する
今回はインプットとしてオリジナル（短縮前）のURLを渡すことで、短縮URLがアウトプットされるAPIをつくります。機能は以下に絞りました。

- **ユーザーから渡されたURLに紐づく短縮URLを発行して、DBに保存する。**
- **DBに登録されている全URLの一覧を取得する。**
- **短縮されたURLから元のURLを取得する。**

## URLとHTTPメソッドを設定する
次の「リクエストパラメータを設計する」とまとめます。
## リクエストパラメータを設計する
**パラメータ**とは「引数」などの意味を持つ英単語で、関数やメソッドなどが呼び出し元から渡された値を受け取るために宣言された変数のことです。

そしてリクエストパラメータ（GETパラメータ）とは、クライアント側からWebサーバ側に送信するデータを、URLの末尾に特定の形式で表示したもののことを指します。

つまりここで求められているのは、どのURLのどのHTTPメソッドのときに何を引数として渡すと、このような動作がされるよ、というURLごとの「入力」「出力」の動きを整理することだと理解しました。

![スクリーンショット 2022-07-25 16.13.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2523354/56dafdaa-6ced-b1cc-4132-825ebd7a3e23.png)
APIサーバの開発ではフロントに反映されるか確認できないので、APIのデータ受け渡しができているかを確認するWebAPI開発用ツールを使うと効果的です。今回は動画で勧められていた[Postman API Platform](https://www.postman.com/)を使いました。

# 開発してみた
ここまで理解したところで、開発に入ってみましょう。
## 使用した環境・技術
- Mac OS
- Python 3.8.9以上
- Flask 2.0.3
- SQLite 3.37.0

PythonとFlaskを使用した理由は、バックエンドの言語としてPythonを学習していたので、その学習内容を踏まえてものづくりをしたかったためです。データベースは今回ローカルでの使用を想定しているため、簡易に使えるライブラリとしてSQLiteを使用しました。

今回のAPIはHTTP通信で、情報の受け渡しには**JSON**（JavaScript Object Notation）の形式を用いています。選択理由としては、調べたなかでJSONが最もメジャーだと判断したからです。

## コードの解説
以下では、JSON形式で値を出力している部分のみコードを書き出しています。全体のコードはこちら[GitHub](https://github.com/lily-engine/short_URL)です。実行環境構築手順もGitHubのREAD MEに記載しました。

### ユーザーから渡されたURLに紐づく短縮URLを発行して、DBに保存する
```
@app.route('/register-url', methods=['POST'])
def get_register_url():
    
    ~~ 省略　　~~

    # データベースに挿入したURLのIDを格納
    url_id = url_data.lastrowid
    # IDから一意のhashを生成
    hashid = hashids.encode(url_id)
    # 短縮URLを作成
    short_url = request.host_url + hashid

    return {
        "short_url": short_url
    }
```

Postmanでの動きはこちらです。GitHubのオリジナルURLを入力しました。
![スクリーンショット 2022-07-31 22.52.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2523354/2df1116b-0d10-e1f8-f5b8-c3d070a1715b.png)
短縮URLが返ってきていることがわかります。

JSON形式では、**「名前」「値」のセットでデータをreturn**します。

なのでここでのreturnは“short_url”という「名前」で“short_url”という「値」を戻しているだけなのですが、すんなりここを理解するまでに最初は時間がかかりました（[この記事](https://amateur-engineer-blog.com/flask-api/)の例がわかりやすかったです）。

### DBに登録されている全URLの一覧を取得する
```
@app.route('/urls')
def urls():

    ~~ 省略　　~~

    return {
        "urls": original_urls
    }
```
![スクリーンショット 2022-07-31 22.52.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2523354/0f890832-3018-9185-2d34-ead60612d4ac.png)
白色の部分は一部画像を加工していますが、"urls"として、データベースに入っているオリジナルURLが全て返ってきています。
### 短縮されたURLから元のURLを取得する
```
@app.route('/<short_url>')
def url_response(short_url):

    ~~ 省略　　~~

        return {
            "original_url": original_url
        }
```
![スクリーンショット 2022-07-31 22.52.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2523354/cdb80dd6-6799-1919-3392-8b0c5398386d.png)
短縮URLからオリジナルURLが戻ってきました。やったね🎉

### その他の部分
SQLiteとの接続やライブラリの使い方など、その他のコードの解説は、先ほど紹介した[](https://ja.getdocs.org/how-to-make-a-url-shortener-with-flask-and-sqlite)ブログをチェックしてください。

この記事に紹介されている「clicks」（短縮URLをクリックされた回数）は、データベースに登録しているものの、現状は受け渡すデータに反映していません。

# 開発してみて
初めてのAPIを開発してみた感想と、いただいたフィードバックを共有します。学習の参考になれば幸いです。
## 開発した感想
この開発にかかった時間は、合計32時間です。

が、途中までは「APIサービス」として開発していたため、いろいろといらない手順がありました。特に、Webページで受け渡していたデータをどうやって変更すればいいのか、調べてもうまく実装できず混乱しました……この経験を踏まえて、今後はもっとスムーズに開発できそうです。慣れている方なら10時間もかからず完成するのかな、と感じました。

バックエンドのものづくり経験がほぼなかった私にとって、初めてAPIサーバを開発する経験は、「こういう仕組みでデータが受け渡しされているのか〜！」と知れておもしろかったです。

総合して考えると、私のようにバックエンドエンジニアを目指したい人にとって、ちょうどいい課題なのでは、と思いました。バックエンドでものづくりするテーマを探している方、おすすめです！

## 改善ポイント
完成したAPIをエンジニアの方に見せたところ、以下のようなフィードバックをいただきました。全体的に「運用」を考える視点が足りなかったことを実感したので、今後に活かしていきます。
- 現状だと1つのオリジナルURLから複数の短縮URLが発行される状態になるので、上書き可能な状態になってしまう。
- 仮にサーバにアップした場合、ローカル環境と本番環境ではオリジナルURLが変更されてしまうので、どちらも対応できるようにする。
- app.pyに拡張性がないので、ファイル分割したりクラスを設計したりして、拡張性を持たせると良い。
- データベースに接続できなかったときの例外処理をしたほうが良い。
# 参考
 - [REST APIとは？ざっくりと理解してみる【初心者向け】](https://tech.012grp.co.jp/entry/rest_api_basics)」
 - [IT用語辞典 e-Words](https://e-words.jp/w/%E3%83%91%E3%83%A9%E3%83%A1%E3%83%BC%E3%82%BF.html)
 - [【Flask】簡単なAPIを実装](https://amateur-engineer-blog.com/flask-api/)
 - [FLASKとSQLITEでURL短縮サービスを作成する方法](https://ja.getdocs.org/how-to-make-a-url-shortener-with-flask-and-sqlite)
 - [小学生でもわかるWebAPI入門。ゼロからWebAPIを作ってみよう](https://amateur-engineer-blog.com/flask-api/)