Node.jsとExpressでWeb APIを作り、ファイルに入出力する。

## EXPRESSを利用して簡単なWeb APIを作成する


### 前提条件
- Node.jsおよびnpmがインストールされている
***
### Express を使用して基本的な Web アプリを作成する

1. 今回作業する用のフォルダを作り、Expressをダウンロードします。
    ```bash:
    mkdir web-app
    cd web-app
    npm init -y
    npm install express
    ```
    <br>
1. ターミナルで```vi app.js```と入力し、app.jsファイルを作成し以下のコードを記述します。
    ```:wq```と入力し上書き保存して終了します。
    ```bash:
    const express = require('express');
    const app = express();
    const port = 3000;
    
    app.get('/', (req, res) => res.send('Hello World!'));
    
    app.listen(port, () => console.log(`Example app listening on port ${port}! http://localhost:${port}/`));
    ```
    <br>・このコードは、express() メソッドを呼び出して、Express アプリケーションの  　インスタンスを作成します。
    ・' / ' 記号へのルートを設定しています。
    ・ルートを設定した後、コードは listen() メソッドを呼び出して Web アプリケー
    　ションを起動します。
    <br>
1. ターミナルで、```node app.js```と入力しExpress Web アプリケーションを開始します。
1. 以下の出力が得られるので、http://localhost:3000/ にアクセスし、出力を確認します。

    ```bash:
    Example app listening on port 3000! http://localhost:3000/
    ```
    この出力は、アプリが起動して実行され、要求を受信する準備ができていることを意味します。
    <br>
1. アクセスした際、ブラウザに以下のように出力されているかを確認します。
    ```bash
    Hello World!
    ```
    <br>
1. Ctrl + Cを押し、Webアプリケーションを終了させます。
<br>

これらはもっとも簡単なAPIとなります。http://localhost:3000/ にアクセスすると、'Hello World! 'が返ってくる、というAPIになります。
    <br>
    
```bash:
    app.get('/', (req, res) => res.send('Hello World!'));
```
　app.js内のこのコードはサーバの「/」にGETメソッドでアクセスしてきたときに、Helloという文字列を返す、というコードになります。GETメソッドというのは、通常私たちがする普通のアクセスです。
　引数のreq, resはそれぞれreq = リクエスト（相手から送られてきた情報）とres = レスポンス（サーバから送る情報）になります。
 　resオブジェクトのsendメソッドを実行することで、相手にデータを送り返すことができます。
***
### JSONを返し、描画する
先ほど'Hello World!'と返すだけだったものを、JSON形式のデータを返し、UIを描画するようにします。

まず、先ほどのapp.jsファイルをJSONのデータを返すように変更します。
```javascript:app.js
const express = require('express');

const app = express();

app.get('/', (req, res) => {
    // クライアントに送るJSONデータ
    const  Data = [
        { title: 'JavaScriptを勉強する', done: true },
        { title: 'Node.jsを勉強する', done: false },
        { title: 'Web APIを作る', done: false }
    ];

    // JSONを送信する
    res.json(todoList);
});

// ポート3000でサーバを立てる
app.listen(3000, () => console.log('Listening on port 3000'));
```
これを実行するとJSON形式のデータがブラウザに表示されると思います。
次は、表示されているデータをもとに、UIを描画してみます。

app.jsを次のように変更します。
```javascript:app.js
const express = require('express');

const app = express();

// webフォルダの中身を公開する
app.use(express.static('web'));

app.get('/api/v1/list', (req, res) => {
    // クライアントに送るJSONデータ
    const  todoList![スクリーンショット 2023-12-15 16.09.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3635457/8dd1ad17-eca6-6574-5665-c8db64a698fe.png)
![スクリーンショット 2023-12-15 16.09.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3635457/3ca7b80f-07ae-cf45-b28b-490db8c2f603.png)
 = [
        { title: 'JavaScriptを勉強する', done: true },
        { title: 'Node.jsを勉強する', done: false },
        { title: 'Web APIを作る', done: false }
    ];

    // JSONを送信する
    res.json(todoList);
});

// ポート3000でサーバを立てる
app.listen(3000, () => console.log('Listening on port 3000'));
```
変更点は```app.use(express.static('web'));```のみです。
これはUIを描画する用のwebフォルダの中身を公開することを意味します。

そして、「web」というフォルダを作り、その中に描画用のindex.htmlファイルを作成します。
そこに以下のように記述します。
```html:index.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>TODOリスト</title>
    <style>
        html {
            background-color: rgb(240, 240, 240);
        }
    </style>
</head>
<body>
    <h1>TODO List</h1>
    <div>
        <ul id="todo-container"></ul>
    </div>

    <script>
        // APIからJSONを取得する
        fetch('./api/v1/list')
            .then((response) => response.json())
            .then((todoList) => {
                // id="todo-container"要素を取得する
                const todoContainer = document.querySelector('#todo-container');

                // コンテナの中身を全部消す
                todoContainer.innerHTML = '';

                // JSONの各要素に対して
                for(const item of todoList) {
                    const li = document.createElement('li');          // リスト要素
                    const label = document.createElement('label');    // ラベル
                    const checkbox = document.createElement('input'); // チェックボックス
                    checkbox.type = 'checkbox';
                    checkbox.checked = item.done;                     // 項目がdoneならチェック
                    const text = new Text(item.title);                // 項目名

                    // ラベルにチェックボックスとテキストを追加する
                    label.appendChild(checkbox);
                    label.appendChild(text);

                    // リスト要素に先ほどのラベルを追加する
                    li.appendChild(label);

                    // TODOリストにリスト要素を追加する
                    todoContainer.appendChild(li);
                }
            })
    </script>
</body>
</html>
```
これで```node app.js```によりサーバを起動し、http://localhost:3000/ にアクセスします。
すると、JSONが描画されていることがわかります。

![スクリーンショット 2023-12-15 16.09.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3635457/e096a75e-0962-90d6-4dc0-aeeb74fd506a.png)

ブラウザを右クリック→検証→ネットワークと進み、リロードして表示される「list」をクリックしてレスポンスタブを開くことで、正常にAPIからJSONデータを受け取っていることがわかります。
![スクリーンショット 2023-12-15 16.15.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3635457/a191ea19-e151-cfa7-79c5-ddc977fabb2f.png)
***
### 作成したTODO LISTにデータを追加する

まずは今後必要になってくるモジュールをインストールしましょう。ブラウザからのデータを解釈するmulterをnpmでインストールします。
```bash:
npm install --save multer
```

次にapp.jsファイルに項目追加用のAPIを追加します。
また、同時にJSONファイルに書き出し、読み込めるようにするため、先ほどの読み込みの部分にも手を加えます。こうすることで、ページを更新したりしてもデータが消えなくなります。
```javascript:app.js
const express = require('express'); // expressモジュールを読み込む
const multer = require('multer'); // multerモジュールを読み込む

const app = express(); // expressアプリを生成する
app.use(multer().none()); // multerでブラウザから送信されたデータを解釈する
app.use(express.static('web')); // webフォルダの中身を公開する

// http://localhost:3000/api/v1/list にアクセスしてきたときに
// TODOリストを返す
app.get('/api/v1/list', (req, res) => {
    // JSONを送信する
const fs = require('fs');

// ファイルのパス
const filePath = 'todolist.json';

// ファイルを非同期で読み込む
fs.readFile(filePath, 'utf8', (err, data) => {
    if (err) {
        console.error('Error reading the file:', err);
        res.status(500).json({ error: 'Internal Server Error' });
        return;
    }

    try {
        // JSONデータをオブジェクトにパース
        const hoo = JSON.parse(data)
        // JSON形式でクライアントに返す
        res.json(hoo);
    } catch (parseError) {
        console.error('Error parsing JSON:', parseError);
        res.status(500).json({ error: 'Internal Server Errorだよ！' });
    }
});

});

// http://localhost:3000/api/v1/add にデータを送信してきたときに
// TODOリストに項目を追加する
app.post('/api/v1/add', (req, res) => {
    // クライアントからの送信データを取得する
    const todoData = req.body;
    const todoTitle = todoData.title;
    
    // ユニークIDを生成する
    const id = new Date().getTime().toString();

    // TODO項目を作る
    const todoItem = {
        id,
        title: todoTitle,
        done: false
    };

    // TODOリストに項目を追加する

    const fs = require('fs');

    // ファイルからJSONデータを読み込む
    const filePath = 'todolist.json';
    const jsonData = fs.readFileSync(filePath, 'utf8');

    // JSONデータをJavaScriptオブジェクトに変換
    const dataObject = JSON.parse(jsonData);

    dataObject.todolist.push(todoItem);

    // 更新されたオブジェクトをJSON形式に変換
    const updatedJsonData = JSON.stringify(dataObject);

    fs.writeFileSync(filePath, updatedJsonData, 'utf8');


    // コンソールに出力する
    console.log('Add: ' + JSON.stringify(todoItem));

    // 追加した項目をクライアントに返す
    res.json(todoItem);
});

// ポート3000でサーバを立てる
app.listen(3000, () => console.log('Listening on port 3000'));
```
最初に読み込んだ```multer```というモジュールによって、ブラウザから送信された内容を解釈できるようにしています。

JSONファイル```todolist.json```を用意し、そこからデータを読み込んだり追加したりするようにしています。

そして/api/v1/addを追加しました。このとき、app.getではなくapp.postを使っています。これは通信にGETメソッドではなくPOSTというメソッドを使うことを示しています。POSTメソッドはその名の通り、クライアントからサーバへ情報を送信するときに使用します。

/api/v1/addでは、クライアント（ブラウザ）から送られてきたデータを取り出し、todoListに追加しています。ついでに今後のために、ユニークなIDを生成して追加しています。

データを追加するときは、まず```todolist.json```のデータを一度javascriptオブジェクトに変換し、そこにクライアントから送られたデータをpushします。
次にそれをJSON形式に変換し、ファイルに書き込んでいます。

```html:index.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>TODOリスト</title>
    <style>
        html {
            background-color: rgb(240, 240, 240);
        }
    </style>
</head>
<body>
    <h1>TODO List</h1>
    <div>
        <ul id="todo-container"></ul>
        <input id="new-todo-item-title"><button id="new-todo-item-add-button">Add</button>
    </div>

    <script>
        // TODOリストを描画する関数
        function renderTodoList(todoList) {
            // id="todo-container"要素を取得する
            const todoContainer = document.querySelector('#todo-container');
            console.log(todoList);
            // コンテナの中身を全部消す
            todoContainer.innerHTML = '';

            // JSONの各要素に対して
            todoList.todolist.forEach(item => {
                const li = document.createElement('li');          // リスト要素
                const label = document.createElement('label');    // ラベル
                const checkbox = document.createElement('input'); // チェックボックス
                checkbox.type = 'checkbox';
                checkbox.checked = item.done;                     // 項目がdoneならチェック
                const text = new Text(item.title);                // 項目名

                // ラベルにチェックボックスとテキストを追加する
                label.appendChild(checkbox);
                label.appendChild(text);

                // リスト要素に先ほどのラベルを追加する
                li.appendChild(label);

                // TODOリストにリスト要素を追加する
                todoContainer.appendChild(li);
                });

            }
        

        // APIからTODOリストを取得して描画する関数
        async function fetchTodoList() {
            // APIからJSONを取得する
            return fetch('./api/v1/list')
                .then((response) => response.json())
                .then((todoList) => {
                    renderTodoList(todoList);
                })
        }

        // APIに新しいTODOアイテムをPOSTする関数
        async function postNewTodoItem(todoItem) {
            // 送信データ'title'にタイトルテキストを追加する
            const body = new FormData();
            body.append('title', todoItem.title);

            // Fetch APIを使って、Web APIにPOSTでデータを送信する
            return fetch('./api/v1/add', {
                method: 'POST', // POSTメソッドで送信する,
                body
            }).then((response) => response.json());
        }

        const newTodoItemTitleInput = document.querySelector('#new-todo-item-title');
        const newTodoAddButton = document.querySelector('#new-todo-item-add-button');

        // Addボタンをクリックしたときに新しいTODO項目をPOSTする
        newTodoAddButton.addEventListener('click', (event) => {
            const title = newTodoItemTitleInput.value;

            // タイトルが空でなければ
            if(title) {
                // 項目をPOSTしたあとにリストを更新する
                postNewTodoItem({title}).then((item) => fetchTodoList());
            }
        });

        // 初回データ読み込み
        fetchTodoList();
    </script>
</body>
</html>
```
TODO項目を追加するための、Addボタンを追加しました。テキストを入力してAddボタンをクリックすると、APIに対してデータをPOSTします。

GET以外のメソッドを使って通信するときは、fetch関数の第二引数で明示的に指定する必要があります。また、POSTするデータはbodyプロパティで指定します。bodyの実体はFormDataです。

Addボタンを押してデータを送信した後、返事が返ってきたらリストを取得し、再描画するようにしています。これで常にリストが最新に保たれます。

```node app.js```と入力し実行し、データを追加することができます。

![スクリーンショット 2023-12-19 15.49.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3635457/04dee7ba-7a5d-5584-52de-66f72b9b0ce9.png)
                ↓
![スクリーンショット 2023-12-19 15.51.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3635457/9ee76abc-ff30-e182-1165-5c67828cfe33.png)


Web APIを通してサーバ側のデータを操作して、クライアント側に反映する、ということが実現できました。今回はサーバ側のファイルに読み書きしているので、一度終了してもデータが消えることはありません。

addボタンを押した後、開発者ツールのネットワークにあるaddをクリックし、ヘッダーを見るとPOSTで通信できていることがわかります。

![スクリーンショット 2023-12-19 15.54.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3635457/be10815a-eeba-d85c-8acc-8520a6399f2a.png)

***
### HTTPメソッドについて
HTTPメソッド（GET、POST、PUT、DELETE）は、クライアントとサーバーの間でリソースの取得や操作を行うための標準化された手段です。

GET:
目的: リソースの取得。
使い分け: サーバーからデータを取得する際に使用します。例えば、ウェブページの読み込みや検索クエリの送信などが該当します。GETリクエストは幾つかのパラメータをURLに含めて送信され、ブラウザの履歴にも残ります。

POST:
目的: リソースの作成または送信。
使い分け: サーバーに新しいデータを送信したり、リソースを作成する際に使用します。例えば、フォームの入力データをサーバーに送信したり、ファイルをアップロードする際にも使用されます。POSTリクエストはデータがリクエストボディに含まれ、URLには表示されません。

PUT:
目的: リソースの更新または作成。
使い分け: 指定されたURIで新しいデータを作成するか、既存のデータを更新する際に使用します。一般的には、完全なリソースを置き換えるために使用されます。PUTリクエストもデータがリクエストボディに含まれます。

DELETE:
目的: リソースの削除。
使い分け: 指定されたURIのリソースを削除する際に使用します。DELETEメソッドを使用すると、対象のリソースが削除されます。


具体的には、以下のような場合に利用されることが考えられます。

GET: ユーザーのプロフィール情報を取得する。
POST: 新しいユーザーアカウントを作成する。
PUT: ユーザーのプロフィール情報を更新する。
DELETE: ユーザーアカウントを削除する。

***

### 参考文献
この記事は以下の情報を参考にして執筆しました。

- [Node.jsとExpressでWeb APIを作ってみよう](https://sbfl.net/blog/2018/08/25/nodejs-express-webapi/)
- [Node.js と Express を使用して Web API を構築する](https://learn.microsoft.com/ja-jp/training/modules/build-web-api-nodejs-express/1-introduction)