「WebAPIの設計手順がわからん」と困ったので調べてみた【初心者】

こんにちは。masakichiです。

先日、業務でWebAPIの設計を任されました。
まだまだ未熟のわたしは、当然、WebAPIの設計などしたことがありません。

**「くっ…WebAPI。使ったことしかねぇよぉ。。。」**

と弱音を吐きながらも、自分なりに設計手順を調べたので、Qiita記事にしようと思いました。

## 対象読者
- WebAPIの設計手順が全くわからない初心者の方
- WebAPIに精通していて、本記事に対してフィードバックをいただける方（歓迎）

## 前提条件
今回作成するのはユーザー情報をあつかうための、シンプルなWebAPIとします。
まずは、とりあえず作って流れを理解しようという目的にしているため、API詳細仕様などは今回の内容には含めていません。

## 序 WebAPIを作る流れ
まずは、WebAPIを作るまでの流れをざっくりと提示します。

本来、データ設計のところなどはもっと因数分解して要件定義をしたほうがいいでしょう。

しかしながら、今回はWebAPIの設計手順を示すことを記事の目的といたしますので、省略しております。

```
1. 画面設計
2. 機能設計
3. 必要なデータの列挙
4. APIの機能一覧を列挙
5. URLとHTTPメソッドを定義する
6. リクエストパラメータを定義する
7. レスポンスを定義する
8. 実際に作る
```

## 画面設計
まず、WebAPIを設計する前に　**Webサービス・アプリの画面設計（UI設計）**　を決めておきます。

今回の例では、ユーザーの一覧表示と新規追加だけのシンプルなものを作ることにします。

![580fd556-6321-4af7-b425-9e794d6a11a3.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1006362/6235c313-069d-0cb1-691c-aa0f98135e7e.jpeg)

この時、考慮することは以下となります。

>各画面で何ができるのか？
表示する情報（文字、画像 など）とレイアウト
上記を画面遷移図にまとめる
引用元：https://qiita.com/Saku731/items/741fcf0f40dd989ee4f8

## 機能設計
続いて、それぞれの画面でできることを定義していきましょう。

この段階で考慮することは以下です。

>裏側の処理（機能名と処理内容）
処理に必要なデータ、データの取得元（画面から入力、DBから取得 など）
処理したデータの受渡し先（画面表示、DBへ保存 など）
引用元：https://qiita.com/Saku731/items/741fcf0f40dd989ee4f8

**ユーザー表示画面**
![30196273-d383-439f-830d-4b09fb51d80f.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1006362/37c83313-9079-1e9b-6f1a-2a6681e332e6.jpeg)


**ユーザー登録画面**
![2bc7fc33-e42e-4bcf-843e-1f2f0f016543.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1006362/80cccea1-cd20-7806-48eb-63409e43ad62.jpeg)


このあと、先程の画面設計と機能設計を紐づけて、フローチャート的なものを作ると、尚よいと思います。

今回はWebAPIを作ることにフォーカスしたいので省略します。

ここまでの流れは、[こちらの記事](https://qiita.com/Saku731/items/741fcf0f40dd989ee4f8)がわかりやすく勉強になりますので、ぜひ読んでみてください。
とてもおすすめです。

## 必要なデータの列挙
機能設計が決まったら、それぞれの機能を満たすために、DBに必要なデータは考慮し、列挙していきましょう。
テーブル名・カラム名も、この段階で決めておくといいでしょう。

今回の場合は以下です。

```
ユーザー情報（users） - テーブル
・ ユーザー名（name） - カラム
・ メールアドレス（mail） - カラム
```

## APIの機能一覧を列挙
次に必要なAPIの機能を列挙していきます。

先程、機能設計で決めた機能名や処理内容を参照しながら決めていきましょう。

今回の場合は、以下2つのAPIが必要になります。

![b2743d58-bdfc-453e-ba5f-86d068a18508.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1006362/ba2f9992-6c99-097d-74d7-d7ecc2b65baa.jpeg)

## URLとHTTPメソッドを定義する
必要なAPIが2つとわかったので、それぞれにURLと、URLにアクセス際のHTTPメソッドを割り当てます。
ようやくAPI設計っぽいフェーズになってきました。

HTTPメソッド？？？という方は、下記の記事のhttpあたりらへんを読むといいと思います。
https://qiita.com/busyoumono99/items/9b5ffd35dd521bafce47

今回は以下の通りとします。

```
・ユーザー一覧を取得
/api/users 【GET】

・ユーザーを新規追加
/api/users 【POST】
```

## リクエストパラメータを定義する
サーバーへリクエストする際のパラメータ（サーバーに送る情報）を定義します。

表記方法は[RESAS-API](https://opendata.resas-portal.go.jp/docs/api/v1/index.html)など外部APIを参考にしましょう。

### ユーザー一覧取得のリクエストパラメータ
```
/api/users 【GET】
```
No parameters

### ユーザー新規追加のリクエストパラメータ
```
/api/users 【POST】
```
| パラメータ | 説明 | 必須 | 備考 |
| --- | --- | --- | --- |
| name | ユーザー名 | ○ | 32文字以下 |
| mail | メールアドレス | ○ | 255文字以下 |

## レスポンスを定義する
リクエストが決まったら、サーバーから返却される情報（レスポンス）を定義します。

実際に返ってくるjsonデータ形式(jsonデータの場合)で書いておくとフロントエンド開発者にとってはわかりやすいと思います。

可能であれば、成功時と失敗時のレスポンスがそれぞれあれば親切です。

### ユーザー一覧取得のレスポンス

```:成功
{
    "status": "OK",
    "users": [
        {
            "id": 1,
            "name": "hoge",
            "mail": "hoge@example.com"
        },
        {
            "id": 2,
            "name": "foo",
            "mail": "foo@example.com"
        },
        {
            "id": 3,
            "name": "bar",
            "mail": "bar@example.com"
        }
    ]
}
```
```:失敗
{
    "status": "error",
    "message": エラーメッセージ
}
```

### ユーザー新規追加のレスポンス

```:成功
{
    "status": "OK",
    "lastID": number
}
```
```:失敗
{
    "status": "error",
    "message": エラーメッセージ
}
```

## 実際に作る
ここまでで、やっと実装の準備が整ったので、実際にWebAPIを作ってみます。

下記記事を参考にNode.jsでAPIサーバー作っていきます。

参考記事はこちら
https://qiita.com/zaburo/items/5d94917d628d9ecd4177

下記手順（というより参考記事のほぼコピペです）

### 準備
```
cd
mkdir webapi
cd webapi

npm init -y

npm install express
npm install sqlite3

touch index.js
```

### サーバーつくる

```javascript:index.js
const express = require("express");
const app = express();

//POSTできたりするように（おまじない）
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

//sqlite3関連設定
const sqlite3 = require("sqlite3");
const db = new sqlite3.Database("./test.db", (err) => {
    if (err) {
        console.error("database error: " + err.message);
    } else {
        db.serialize(() => {
            //都度table削除（あれば）
            db.run("drop table if exists users");
            //table生成（無ければ）
            db.run("create table if not exists users( \
                id integer primary key autoincrement, \
                name nverchar(32), \
                mail nverchar(255) \
            )", (err) => {
                if (err) {
                    console.error("table error: " + err.message);
                } else {
                    //初期データinsert
                    db.run("insert into users(name,mail) values(?,?)", "hoge", "hoge@example.com");
                    db.run("insert into users(name,mail) values(?,?)", "foo", "foo@example.com");
                    db.run("insert into users(name,mail) values(?,?)", "bar", "bar@example.com");
                }
            });
        });
    }
});

//リッスン開始
app.listen(3000, () => {
    console.log("Start server on port 3000.");
});

//create
app.post("/api/users", (req, res) => {
    const reqBody = req.body;
    const stmt = db.prepare("insert into users(name,mail) values(?,?)"); //lastID取得のため
    stmt.run(reqBody.name, reqBody.mail, (err, result) => { //lambda式を使うとthis.lastIDでは取得できない
        if (err) {
            res.status(400).json({
                "status": "error",
                "message": err.message
            });
            return;
        } else {
            res.status(201).json({
                "status": "OK",
                "lastID": stmt.lastID
            });
        }
    });
});

//get users
app.get("/api/users", (req, res) => {
    db.all("select * from users", [], (err, rows) => {
        if (err) {
            res.status(400).json({
                "status": "error",
                "message": err.message
            });
            return;
        } else {
            res.status(200).json({
                "status": "OK",
                "users": rows
            });
        }
    });
});
```

### 実行してみる

```text:サーバー起動
node index.js
```

まずはユーザー一覧

```text:ユーザー一覧取得
curl -s -X GET http://localhost:3000/api/users
```

値が返ってきました。
```text:response
{
	"status": "OK",
	"users": [
		{
			"id": 1,
			"name": "hoge",
			"mail": "hoge@example.com"
		},
		{
			"id": 2,
			"name": "foo",
			"mail": "foo@example.com"
		},
		{
			"id": 3,
			"name": "bar",
			"mail": "bar@example.com"
		}
	]
}
```

次にユーザー追加

```text:ユーザー追加
curl -s -X POST -H "Content-Type: application/json" -d '{"name":"test","mail":"test@example.com"}' http://localhost:3000/api/users
```

こっちも無事に返ってきました

```text:成功
{
	"status": "OK",
	"lastID": 4
}
```

## おわりに
今回、初めてWebAPIの設計について考えてみて、普段APIを使うだけよりも更に勉強になったなと感じます。

フロントエンドエンジニアとして、APIまわりの知識は必須の時代かと思うので、今後もさまざまAPIサイトを見ながら、仕様などについても勉強していきます。

この記事が少しでも、わたしのような同志の役にたてば嬉しいです。

## 参考リンク先
下記リンク先には大変お世話になりました。
感謝申し上げます。

- [小学生でもわかるWebAPI入門。ゼロからWebAPIを作ってみよう](https://www.youtube.com/watch?v=6_zIN-bByB4)
- [要件定義～システム設計ができる人材になれる記事](https://qiita.com/Saku731/items/741fcf0f40dd989ee4f8)
- [node.js + express + sqlite3でWebAPI（CRUD）を作る](https://qiita.com/zaburo/items/5d94917d628d9ecd4177)