EXPRESSを利用して簡単なWeb APIを作成する
前提条件
- Node.jsおよびnpmがインストールされている
Express を使用して基本的な Web アプリを作成する
-
今回作業する用のフォルダを作り、Expressをダウンロードします。
mkdir web-app cd web-app npm init -y npm install express
-
ターミナルで
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 アプリケー
ションを起動します。
-
ターミナルで、
node app.js
と入力しExpress Web アプリケーションを開始します。 -
以下の出力が得られるので、http://localhost:3000/ にアクセスし、出力を確認します。
Example app listening on port 3000! http://localhost:3000/
この出力は、アプリが起動して実行され、要求を受信する準備ができていることを意味します。
-
アクセスした際、ブラウザに以下のように出力されているかを確認します。
Hello World!
-
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のデータを返すように変更します。
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を次のように変更します。
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ファイルを作成します。
そこに以下のように記述します。
<!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が描画されていることがわかります。
ブラウザを右クリック→検証→ネットワークと進み、リロードして表示される「list」をクリックしてレスポンスタブを開くことで、正常にAPIからJSONデータを受け取っていることがわかります。
作成したTODO LISTにデータを追加する
まずは今後必要になってくるモジュールをインストールしましょう。ブラウザからのデータを解釈するmulterをnpmでインストールします。
npm install --save multer
次にapp.jsファイルに項目追加用のAPIを追加します。
また、同時にJSONファイルに書き出し、読み込めるようにするため、先ほどの読み込みの部分にも手を加えます。こうすることで、ページを更新したりしてもデータが消えなくなります。
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形式に変換し、ファイルに書き込んでいます。
<!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
と入力し実行し、データを追加することができます。
Web APIを通してサーバ側のデータを操作して、クライアント側に反映する、ということが実現できました。今回はサーバ側のファイルに読み書きしているので、一度終了してもデータが消えることはありません。
addボタンを押した後、開発者ツールのネットワークにあるaddをクリックし、ヘッダーを見るとPOSTで通信できていることがわかります。
HTTPメソッドについて
HTTPメソッド(GET、POST、PUT、DELETE)は、クライアントとサーバーの間でリソースの取得や操作を行うための標準化された手段です。
GET:
目的: リソースの取得。
使い分け: サーバーからデータを取得する際に使用します。例えば、ウェブページの読み込みや検索クエリの送信などが該当します。GETリクエストは幾つかのパラメータをURLに含めて送信され、ブラウザの履歴にも残ります。
POST:
目的: リソースの作成または送信。
使い分け: サーバーに新しいデータを送信したり、リソースを作成する際に使用します。例えば、フォームの入力データをサーバーに送信したり、ファイルをアップロードする際にも使用されます。POSTリクエストはデータがリクエストボディに含まれ、URLには表示されません。
PUT:
目的: リソースの更新または作成。
使い分け: 指定されたURIで新しいデータを作成するか、既存のデータを更新する際に使用します。一般的には、完全なリソースを置き換えるために使用されます。PUTリクエストもデータがリクエストボディに含まれます。
DELETE:
目的: リソースの削除。
使い分け: 指定されたURIのリソースを削除する際に使用します。DELETEメソッドを使用すると、対象のリソースが削除されます。
具体的には、以下のような場合に利用されることが考えられます。
GET: ユーザーのプロフィール情報を取得する。
POST: 新しいユーザーアカウントを作成する。
PUT: ユーザーのプロフィール情報を更新する。
DELETE: ユーザーアカウントを削除する。
参考文献
この記事は以下の情報を参考にして執筆しました。