1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

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

Posted at

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

前提条件

  • Node.jsおよびnpmがインストールされている

Express を使用して基本的な Web アプリを作成する

  1. 今回作業する用のフォルダを作り、Expressをダウンロードします。

    mkdir web-app
    cd web-app
    npm init -y
    npm install express
    

  2. ターミナルでvi app.jsと入力し、app.jsファイルを作成し以下のコードを記述します。
    :wqと入力し上書き保存して終了します。

    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}/`));
    


    ・このコードは、express() メソッドを呼び出して、Express アプリケーションの  インスタンスを作成します。
    ・' / ' 記号へのルートを設定しています。
    ・ルートを設定した後、コードは listen() メソッドを呼び出して Web アプリケー
     ションを起動します。

  3. ターミナルで、node app.jsと入力しExpress Web アプリケーションを開始します。

  4. 以下の出力が得られるので、http://localhost:3000/ にアクセスし、出力を確認します。

    Example app listening on port 3000! http://localhost:3000/
    

    この出力は、アプリが起動して実行され、要求を受信する準備ができていることを意味します。

  5. アクセスした際、ブラウザに以下のように出力されているかを確認します。

    Hello World!
    

  6. Ctrl + Cを押し、Webアプリケーションを終了させます。


これらはもっとも簡単なAPIとなります。http://localhost:3000/ にアクセスすると、'Hello World! 'が返ってくる、というAPIになります。

    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のデータを返すように変更します。

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を次のように変更します。

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ファイルを作成します。
そこに以下のように記述します。

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

ブラウザを右クリック→検証→ネットワークと進み、リロードして表示される「list」をクリックしてレスポンスタブを開くことで、正常にAPIからJSONデータを受け取っていることがわかります。
スクリーンショット 2023-12-15 16.15.07.png


作成したTODO LISTにデータを追加する

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

npm install --save multer

次にapp.jsファイルに項目追加用のAPIを追加します。
また、同時にJSONファイルに書き出し、読み込めるようにするため、先ほどの読み込みの部分にも手を加えます。こうすることで、ページを更新したりしてもデータが消えなくなります。

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形式に変換し、ファイルに書き込んでいます。

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

スクリーンショット 2023-12-19 15.51.27.png

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

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

スクリーンショット 2023-12-19 15.54.12.png


HTTPメソッドについて

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

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

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

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

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

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

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


参考文献

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

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?