はじめに
私はAPIを作成したことがなく、サーバーサイドの知見が少ないです。APIを理解するために、Node.jsを用いてローカルでTODOリストを管理するAPIを作成します。初学者の私がAPIを作成することを目的としており、GET POST メソッドなどの基本的な部分に着目して記載しています。そのため、今回はデータの永続化などは考慮していません。
セットアップ
まずは、新規プロジェクトを作成します。
新しいディレクトリを作成
mkdir todo-api
ディレクトリ内へ
cd todo-api
プロジェクトをセットアップ
npm init -y
出力結果) package.jsonが生成される
Wrote to /Users/(自身のPCのディレクトリ)/ReactNativeProject/todo-api/package.json:
{
"name": "todo-api",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
Express.jsをインストール
npm install express
出力結果) 問題なくインストール完了
added 62 packages, and audited 63 packages in 2s
11 packages are looking for funding
run `npm fund` for details
found 0 vulnerabilities
実装
app.jsファイルを新規作成して、実装していく。
Expressのインポート
const express = require('express');
const app = express();
const port = 3000;
-
require('express')
はNode.jsのモジュールシステムを使用して、'express'を読み込んでいます -
app
でexpress
をインスタンス生成します -
port
でポート番号の値を保持しています
ToDoリストのデータ格納先
const todos = [
{ id: 1, title: 'タスク1', completed: false },
{ id: 2, title: 'タスク2', completed: true },
];
- 今回はデータの永続化を図らずに、
todos
でデータを保持することとする。そのため、ローカルサーバー起動時はデータを保持するが、落とすと初期値に戻ってしまいます
app.use(express.json());
-
app.use()
は、Expressアプリケーションにミドルウェアを追加するためのメソッドです。ミドルウェアはリクエストとレスポンスの間に挿入され、特定の処理を行うために使用されます。ミドルウェアとは、GET
POST
メソッドなどを実行する前に呼び出される処理だと理解すれば良いと思います
詳しくは以下のリンクを御覧ください。
- APIメソッドを実行する前に、クライアントからのHTTPリクエストのボディに含まれるJSONデータを変換し、JavaScriptオブジェクトに変換する実装をしています
ToDoリストの取得
app.get('/todos', (req, res) => {
res.json(todos);
});
-
app.get('/todos', (req, res) => { ... );
は、ExpressでGETリクエストが '/todos' パスに対して届いた時の処理を定義しています -
(req, res) => { ... }
は、req はリクエストで、クライアントからのリクエストに関する情報を含んでいます。res はレスポンスで、クライアントに返すためのレスポンスを操作するためのメソッドを提供します -
res.json(todos);
は、レスポンスをJSON形式で送信するコードです。これがクライアントに返されます
ToDoリストの作成
app.post('/todos', (req, res) => {
const newTodo = req.body;
todos.push(newTodo);
res.status(201).json(newTodo);
});
-
app.post('/todos', (req, res) => { ... });
は、ExpressでPOSTリクエストが '/todos' パスに対して届いた時の処理を定義しています -
(req, res) => { ... }
は、GETと同様です -
const newTodo = req.body
; は、クライアントが送信した新しいToDoデータを保持しています -
todos.push(newTodo);
は、todos
配列に新しいToDoデータnewTodo
を追加します -
res.status(201).json(newTodo);
は、レスポンスを設定しています。まず、HTTPステータスコードを201に設定し、newTodo
をJSON形式でクライアントに返信します
ToDoリストの更新
app.put('/todos/:id', (req, res) => {
const todoId = parseInt(req.params.id);
const updatedTodo = req.body;
const todoIndex = todos.findIndex(todo => todo.id === todoId);
if (todoIndex !== -1) {
todos[todoIndex] = { ...todos[todoIndex], ...updatedTodo };
res.json(todos[todoIndex]);
} else {
res.status(404).json({ message: '指定されたToDoが見つかりません' });
}
});
-
app.put();
は、ExpressでPUTリクエストが/todos/:id
パスに対しての処理を定義しています。:id
は動的なパラメータとして、識別子(ID)を受け取るために設定しています -
const todoId = parseInt(req.params.id);
は、リクエストのパスパラメータである:id
を整数に変換して、todoId
という変数に格納しています。これにより、IDを取得します
パラメーターを設定する際に、パスパラメーターとクエリパラメーターがあります。両者の違いに関しては参考になったリンク貼っておきます。
-
const updatedTodo = req.body;
は、クライアントからのリクエストボディ(JSONデータ)を取得し、updatedTodo という変数に格納しています -
const todoIndex = todos.findIndex(todo => todo.id === todoId);
は、 todos 配列内で指定されたToDoのIDを持つToDoを検索し、そのインデックスを todoIndex 変数に格納しています
findIndex
は、値が見つからなかった場合に-1を返すメソッドです。以下リンクです。
-
todoIndex
の値に応じて処理が分岐されます。もしtodoIndex
が -1 でない場合、指定されたToDoが見つかったことを意味し、ToDoデータが更新され、更新後のToDoデータがクライアントにJSON形式で返されます -
もし
todoIndex
が -1 の場合、指定されたToDoが見つからなかったことを意味し、HTTPステータスコード404が設定され、エラーメッセージを含むJSONレスポンスがクライアントに返されます
ToDoリストの削除
app.delete('/todos/:id', (req, res) => {
const todoId = parseInt(req.params.id);
const todoIndex = todos.findIndex(todo => todo.id === todoId);
if (todoIndex !== -1) {
const deletedTodo = todos.splice(todoIndex, 1)[0];
res.json(deletedTodo);
} else {
res.status(404).json({ message: '指定されたToDoが見つかりません' });
}
});
-
app.delete('/todos/:id', (req, res) => { ... });
は、ExpressアプリケーションでDELETEリクエストが '/todos/:id' パスに対して受け付けられた場合の処理を定義しています。':id' は動的なパラメータとして、ToDoの一意の識別子(ID)を受け取るために使用されます -
識別子の取得方法や、該当の識別子が存在するかの確認、条件分岐に関しては、PUTの項目と同様なので省略します
-
splice(todoIndex, 1)
は、todos
配列からtodoIndex
で指定された位置から1つの要素を削除します。このメソッドは、元の配列 を変更し、削除された要素 を含む 新しい配列を返します
筆者はJavaScriptを書いたことがなかったので、上記の太字の部分は理解に戸惑った部分でした。
-
[0]
は、splice
メソッドによって返される新しい配列から最初の要素(削除されたToDo)を取得し、それをres
で返しています
サーバーを起動
app.listen(port, () => {
console.log(`Server is running on port ${port}`);
});
-
app.listen(port, () => { ... });
は、port
で指定したポート番号でクライアントからのリクエストを待ち受けるためのコードです。この行のコードはアプリケーションのメインエントリポイントの最後に配置され、サーバーを起動する役割を果たします -
port
は、ポート番号を指定するための変数です。最初の方の実装で、const port = 3000;
というように設定しているので、サーバーはポート3000でアクセス可能です -
console.log(Server is running on port ${port});
は、サーバーが正常に起動したことをコンソールにログ出力するコードです。ログメッセージにはサーバーがどのポートでリッスンしているかが表示されます
APIの挙動確認
ローカルサーバーを起動
node app.js
GET
初期のTODOリストの値を確認する。
curl http://localhost:3000/todos
[
{"id":1,"title":"タスク1","completed":false}, ← OK
{"id":2,"title":"タスク2","completed":true} ← OK
]
POST
新規のTODOリストの値を追加する。
curl -X POST -H "Content-Type: application/json" -d '{"title": "新しいTo-Doのタイトル", "completed": false}' http://localhost:3000/todos
{"title":"新しいTo-Doのタイトル","completed":false}%
追加されたか確認
curl http://localhost:3000/todos
[
{"id":1,"title":"タスク1","completed":false},
{"id":2,"title":"タスク2","completed":true},
{"title":"新しいTo-Doのタイトル","completed":false} ← 追加されている
]
PUT
すでに存在する値を更新する
curl -X PUT -H "Content-Type: application/json" -d '{"title": "更新後のTo-Doのタ イトル", "completed": true}' http://localhost:3000/todos/1
{"id":1,"title":"更新後のTo-Doのタイトル","completed":true}
更新されたか確認
curl http://localhost:3000/todos
[
→ {"id":1,"title":"更新後のTo-Doのタイトル","completed":true},← 更新されている
{"id":2,"title":"タスク2","completed":true},
{"title":"新しいTo-Doのタイトル","completed":false}
]
DELETE
すでに存在する値を削除する
curl -X DELETE http://localhost:3000/todos/1
{"id":1,"title":"更新後のTo-Doのタイトル","completed":true}%
削除されたか確認
curl http://localhost:3000/todos
[
← 削除されている
{"id":2,"title":"タスク2","completed":true},
{"title":"新しいTo-Doのタイトル","completed":false}
]
上記の検証により、正しく挙動が行えていることを確認できた。
まとめ
基本的なAPIの構成を、一行一行コードを追いながら学習しました。今回はDBに保存しておらず、一時的に変数の中に保存しているのみであるため、次回以降ではFirestoreやRealmなどを用いてデータの永続化も考慮したAPIを作成してみようかと思いました。
最後に
他にも良い方法があれば、コメントいただけると大変うれしいです。
良かったと思ったら、いいねやTwitterのフォローよろしくお願いいたします!
個人でアプリを作成しているので、良かったら覗いてみてください!