Node.jsとExpressでREST APIを作成していこうと思います。
昨今のアプリケーションは、APIが必ずと言っていいほど存在します。
有名な例だと、Twitter APIなどでしょうか。様々な企業・サービスからAPIが提供されているので、興味があれば色々調べてみるのもいいと思います!
学習に使った本はこちら!
Amazon Kindle Unlimited でもNode.jsの本はいっぱいあるんじゃ!!
今回は、データベースも使いますので、Node.jsとExpressでデータベースの使い方がわからない方は、こちらの記事から先に実施することをお勧めします!
データベースの環境がない方は、Herokuで無料で使えるPostgreSQLを使うか、ローカルにお好きなデータベースを構築してください!(本記事は、Heroku Postgresを使用しております。)
アプリケーションの基礎になる部分ですので、ぜひ参考にしてみてください!
- Node.jsとExpressでサーバサイドアプリケーションを開発したい!
- Node.jsとExpressでREST APIを開発したい!
そもそもREST APIってなに??
REST APIってなんじゃ?
早速開発していきましょう!!と言いたいところですが、そもそもREST APIとは何かと言うことを簡単に説明しておこうと思います!
簡単にしか説明しませんが、もっと詳しく知りたい方は、ぜひ調べてみてください!
- API(Application Programming Interface)
-
異なるソフトウェア・アプリケーションがお互いにやりとりするために使用するインタフェースのこと
- WebAPI
-
APIの利用を、 HTTP/HTTPSで実現するAPIのことをWeb APIと呼びます。
Web APIではないAPIは、APIの利用側が用いるプログラミング言語と同じ言語で提供される事が多いです。(厳密には違いますが、Web APIではないAPIはライブラリのようなものと認識してもいいかもしれません。)
ここまでは、前々回のこちらの記事でも説明したと思います。
Web API の中にも、実装方式としてREST APIやSOAP API などが存在しています。
- SOAP(Simple Object Access Protocol)
-
XMLデータのやり取りを行うRPCプロトコルです。
RPC(Remote Procedure Call)は、別のプロセス(別コンピュータ)に対して、メソッド呼び出しを行うものを指しています。
なので、あくまで別環境のメソッド呼び出しを行うものに過ぎないです。 - REST API(REpresentational State Transfer)
-
URIでリソースを一意に識別し、リソースの操作はHTTPメソッドで指定して操作する実装方式です。
セッション管理や状態管理は行わない(ステートレス)で、同じURIの呼び出し結果は、常に同じ結果が返されることが必要です。結果は、JSONやXML、HTMLで返されますが、JSONが主流のようです。
処理結果はHTTPステータスコードで通知もします。リソース(データ)へアクセスしたい場合の実装方針です。
文章で説明してもいまいちパッとしないと思いますが、今回はmemoデータを管理するためのREST APIを作成していきます。
実際に作業をしていけば、ある程度理解できると思うのでその後で改めて調べてみるのもいいかなと思います。
REST APIを定義していこう!
では、REST APIを定義していこうと思います。
定義とは言っても、メソッドとURIを決めるだけなのですが。。。
- URIって何?
-
URIは、URL(Web上のどこにあるか)とURN(Web上にあるファイル名)の総称です。
実際のところ、URLやURIをしっかり区別している人を見たことがないのでURI=URLぐらいに考えてもいいと思います。
Webページ(https://teech-lab.com)は、URIでありURLなんです。https://はURIの一部になります。
今回は、こんな感じで開発していこうと思います!
URI | メソッド | 説明 |
---|---|---|
/memo | GET | メモデータを全件取得する。 |
/memo/[ID] | GET | IDのメモデータを取得する。 |
/memo | POST | メモデータを新規登録する。 |
/memo/[ID] | PUT | IDのメモデータを更新する。 |
/memo/[ID] | DELETE | IDのメモデータを削除する。 |
REST APIとして認められない例も載せておきます。
REST APIとして開発する場合は、下記のようにならないように気をつけてください。
URI | メソッド | 説明 |
---|---|---|
/get-memo | GET | メモデータを全件取得する。 |
/get-memo/[ID] | GET | IDのメモデータを取得する。 |
/create-memo | GET | メモデータを新規登録する。 |
/update-memo/[ID] | GET | IDのメモデータを更新する。 |
/delete-memo/[ID] | GET | IDのメモデータを削除する。 |
GETでメモデータを取得するREST APIを実装しよう!
では、やっとですが実装していきましょう!
GETはデータを取得する場合に使われるHTTPメソッドです。
全件取得するREST APIを実装!
前回までのフォルダ構成は、こんな感じです。
今回は、memo.jsのみを修正していきます。
pgでデータベースに接続し、データを全件取得しています。
エラーなら、HTTPステータス500でエラーメッセージを返却し、正常なら全データを返却します。
データがない場合は、空の配列がレスポンスとして返却されます。
memo.js
var express = require('express')
var router = express.Router()
// pool.jsを読み込み
const pool = require('../db/pool')
/**
* メモを全件取得するAPI
* @returns {Object[]} data
* @returns {number} data.id - ID
* @returns {string} data.title - タイトル
* @returns {string} data.text - 内容
*/
router.get('/', function (req, res, next) {
// データベースから全件取得する
pool.query('SELECT * FROM memo', function (error, results) {
// エラー
if (error) {
res.status(500).json({
status: '500 Internal Server Error',
error: error,
})
}
// 正常なら取得したデータを返却
res.status(200).json({
data: results.rows,
})
})
})
module.exports = router
IDを指定してデータを取得するREST APIを実装!
続いて、パラメータでIDを指定してIDに紐づくメモを取得するREST APIを実装しましょう!
先ほどのmemo.jsに追記します!
SELECT文にWHEREを追加してIDで絞り込みます。
データがない場合は、空の配列がレスポンスとして返却されます。
memo.js
var express = require('express')
var router = express.Router()
// pool.jsを読み込み
const pool = require('../db/pool')
/**
* メモを全件取得するAPI
* @returns {Object[]} data
* @returns {number} data.id - ID
* @returns {string} data.title - タイトル
* @returns {string} data.text - 内容
*/
router.get('/', function (req, res, next) {
// データベースから全件取得する
pool.query('SELECT * FROM memo', function (error, results) {
// エラー
if (error) {
res.status(500).json({
status: '500 Internal Server Error',
error: error,
})
}
// 正常なら取得したデータを返却
res.status(200).json({
data: results.rows,
})
})
})
/**
* 指定されたIDのメモを取得するAPI
* @param {number} id - メモID
* @returns {Object[]} data
* @returns {number} data.id - ID
* @returns {string} data.title - タイトル
* @returns {string} data.text - 内容
*/
router.get('/:id', function (req, res, next) {
// パスパラメータを取得
const { id } = req.params
// データベースから取得する
pool.query('SELECT * FROM memo WHERE id = $1', [id], function (
error,
results,
) {
// エラー
if (error) {
res.status(500).json({
status: '500 Internal Server Error',
error: error,
})
}
// 正常なら取得したデータを返却
res.status(200).json({
data: results.rows,
})
})
})
module.exports = router
GETを確認してみよう!
ここまでできたら、確認してみましょう!
下記のように設定して、データベースに登録されている全データが取得できていればOKです!
(私の場合は、id=1,4ですが、これは削除したデータがあるためです。)
URI | メソッド | 説明 |
---|---|---|
/memo | GET | メモデータを全件取得する。 |
続いて、IDを指定してメモデータを取得してみましょう!
下のように設定して、指定したIDのデータを取得できていればOKです!
URI | メソッド | 説明 |
---|---|---|
/memo/[ID] | GET | IDのメモデータを取得する。 |
存在しないIDのメモデータを取得しようとした場合、空の配列が返却されると思います。
こちらは、エラーにしてもいいですし、NULL等にしてしまってもいいと思います。
続いて、エラーの場合にエラーレスポンスが返却されることを確認しましょう!
エラーにするために、DBの接続情報(pool.jsに記載のある情報)を適当に書き換えます。
画像は、全件取得の場合ですが全件取得もID取得も両方確認しておきましょう!
画像のようになっていればOKです!
LogにHTTPステータスコード304が表示されることがあると思います。
リクエストの対象データが前回のリクエスト時から更新されていないことを意味しています。
この場合、前回アクセスした時のブラウザ内のキャッシュにあるデータを取り出します。
サーバーからデータをダウンロードする処理が行われないので、より高速になります。
POSTでメモデータを登録するREST APIを実装しよう!
続いて、POSTでメモデータを登録してみましょう!!
POSTは、データを登録する際に使用されるHTTPメソッドです!
データを新規登録するREST APIを実装!
先ほど、GETで編集したmemo.jsに下記を追加します。
Bodyからtitleとtextを取得して、INSERTでデータベースに登録します。
登録が成功したら、HTTPステータス201でsuccessを返却します。
(HTTPステータスは、200でもOKです!)
memo.js(抜粋)
/**
* メモを新規登録するAPI
* @returns {string} status - success
*/
router.post('/', function (req, res, next) {
// bodyからtitleとtextを取得
const { title, text } = req.body.memo
// データベースに登録する
pool.query(
'INSERT INTO memo(title, text) VALUES ($1, $2)',
[title, text],
function (error, results) {
if (error) {
res.status(500).json({
status: '500 Internal Server Error',
error: error,
})
}
// 登録できたらsuccessを返却
res.status(201).json({
status: 'success',
})
},
)
})
POSTを確認してみよう!
では、POSTも動作確認してみましょう!
URI | メソッド | 説明 |
---|---|---|
/memo | POST | メモデータを新規登録する。 |
Bodyには、下記のようなJSONを指定しています。
画像のように、successが帰って来ればOKです。この後に、ちゃんと登録されているかGETしてみてもいいと思います!
{
"memo": {
"id": 1,
"text": "text"
}
}
PUTでメモデータを更新するREST APIを実装しよう!
続いて、PUTでメモデータを更新してみましょう!!
PUTは、データを更新する際に使用されるHTTPメソッドです!
部分的な更新はPATCHを使うこともあるのですが、メソッドが多くなっても管理がめんどくさいので更新はPUTにしています。PATCHの場合もメソッドの変更だけなので本質的な部分に違いはありません。
データを更新するREST APIを実装!
こちらもmemo.jsに追記していきます!
パラメータからIDを取得し、Bodyからtitleとtextを取得します!
更新が成功したら、HTTPステータス200でsuccessを返却し、IDで指定したデータが存在しない場合は、HTTPステータス400を返却します。
memo.js(抜粋)
/**
* 指定されたIDのメモを更新するAPI
* @param {number} id - メモID
* @returns {string} status - success
*/
router.put('/:id', function (req, res, next) {
// paramaterからidを取得
const { id } = req.params
// bodyからtitleとtextを取得
const { title, text } = req.body.memo
// データベースを更新する
pool.query(
'UPDATE memo SET title = $1, text = $2 WHERE id = $3',
[title, text, id],
function (error, results) {
if (error) {
res.status(500).json({
status: '500 Internal Server Error',
error: error,
})
}
console.log(results);
if (results.rowCount === 0) {
// 更新するデータがなかった場合
res.status(400).json({
status: '400 Bad Request',
message: 'データが存在しません。',
})
} else {
// 更新できたらsuccessを返却
res.status(200).json({
status: 'success',
})
}
},
)
})
PUTを確認してみよう!
では、PUTも確認してみましょう!
URI | メソッド | 説明 |
---|---|---|
/memo/[ID] | PUT | IDのメモデータを更新する。 |
Bodyには、POSTと同じ形式のJSONを指定して、更新したいIDをパスパラメータで指定します!
画像のように返却されていればOKです!(GETで確認してみてくださいね!)
次に、存在しないIDを指定して更新しようとした場合を確認します。
400 Bad RequestになっていればOKです!
DELETEでメモデータを削除するREST APIを実装しよう!
最後に、DELETEでメモデータを削除してみましょう!!
DELETEは、データを削除する際に使用されるHTTPメソッドです!
データを削除するREST APIを実装!
こちらもmemo.jsに追記していきます!
パラメータからIDを取得します!
削除が成功したら、HTTPステータス200でsuccessを返却し、IDで指定したデータが存在しない場合は、HTTPステータス400を返却します。
memo.js(抜粋)
/**
* 指定されたIDのメモを削除するAPI
* @param {number} id - メモID
* @returns {string} status - success
*/
router.delete('/:id', function (req, res, next) {
// paramaterからidを取得
const { id } = req.params
// データベースから削除する
pool.query('DELETE FROM memo WHERE id = $1', [id], function (error, results) {
if (error) {
res.status(500).json({
status: '500 Internal Server Error',
message: error,
})
}
if (results.rowCount === 0) {
// 削除するデータがなかった場合
res.status(400).json({
status: '400 Bad Request',
message: 'データが存在しません。',
})
} else {
// 削除できたらsuccessを返却
res.status(200).json({
status: 'success',
})
}
})
})
DELETEを確認してみよう!
DELETEも同じように確認します!
URI | メソッド | 説明 |
---|---|---|
/memo/[ID] | DELETE | IDのメモデータを削除する。 |
削除したいIDをパスパラメータで指定します。
下の画像のようになっていればOKです!
次に、存在しないIDを指定して削除しようとした場合を確認します。
400 Bad RequestになっていればOKです!
これは、400でなくて200でもいいかなと思いますので、要件次第で変えてもらえればと思います。
memo.jsの全コード
今までのコードをまとめたものを一応載せておきます!
memo.js
var express = require('express')
var router = express.Router()
// pool.jsを読み込み
const pool = require('../db/pool')
/**
* メモを全件取得するAPI
* @returns {Object[]} data
* @returns {number} data.id - ID
* @returns {string} data.title - タイトル
* @returns {string} data.text - 内容
*/
router.get('/', function (req, res, next) {
// データベースから全件取得する
pool.query('SELECT * FROM memo', function (error, results) {
// エラー
if (error) {
res.status(500).json({
status: '500 Internal Server Error',
error: error,
})
}
// 正常なら取得したデータを返却
res.status(200).json({
data: results.rows,
})
})
})
/**
* 指定されたIDのメモを取得するAPI
* @param {number} id - メモID
* @returns {Object[]} data
* @returns {number} data.id - ID
* @returns {string} data.title - タイトル
* @returns {string} data.text - 内容
*/
router.get('/:id', function (req, res, next) {
// パスパラメータを取得
const { id } = req.params
// データベースから取得する
pool.query('SELECT * FROM memo WHERE id = $1', [id], function (
error,
results,
) {
// エラー
if (error) {
res.status(500).json({
status: '500 Internal Server Error',
error: error,
})
}
// 正常なら取得したデータを返却
res.status(200).json({
data: results.rows,
})
})
})
/**
* メモを新規登録するAPI
* @returns {string} status - success
*/
router.post('/', function (req, res, next) {
// bodyからtitleとtextを取得
const { title, text } = req.body.memo
// データベースに登録する
pool.query(
'INSERT INTO memo(title, text) VALUES ($1, $2)',
[title, text],
function (error, results) {
if (error) {
res.status(500).json({
status: '500 Internal Server Error',
error: error,
})
}
// 登録できたらsuccessを返却
res.status(201).json({
status: 'success',
})
},
)
})
/**
* 指定されたIDのメモを更新するAPI
* @param {number} id - メモID
* @returns {string} status - success
*/
router.put('/:id', function (req, res, next) {
// paramaterからidを取得
const { id } = req.params
// bodyからtitleとtextを取得
const { title, text } = req.body.memo
// データベースを更新する
pool.query(
'UPDATE memo SET title = $1, text = $2 WHERE id = $3',
[title, text, id],
function (error, results) {
if (error) {
res.status(500).json({
status: '500 Internal Server Error',
error: error,
})
}
console.log(results);
if (results.rowCount === 0) {
// 更新するデータがなかった場合
res.status(400).json({
status: '400 Bad Request',
message: 'データが存在しません。',
})
} else {
// 更新できたらsuccessを返却
res.status(200).json({
status: 'success',
})
}
},
)
})
/**
* 指定されたIDのメモを削除するAPI
* @param {number} id - メモID
* @returns {string} status - success
*/
router.delete('/:id', function (req, res, next) {
// paramaterからidを取得
const { id } = req.params
// データベースから削除する
pool.query('DELETE FROM memo WHERE id = $1', [id], function (error, results) {
if (error) {
res.status(500).json({
status: '500 Internal Server Error',
message: error,
})
}
if (results.rowCount === 0) {
// 削除するデータがなかった場合
res.status(400).json({
status: '400 Bad Request',
message: 'データが存在しません。',
})
} else {
// 削除できたらsuccessを返却
res.status(200).json({
status: 'success',
})
}
})
})
module.exports = router
まとめ
今回は、REST APIを実装してみました!
このAPIがあれば、メモアプリは作ることができると思います!
こちらを参考に色々カスタマイズしてみてください。
学習に使った本はこちら!
Amazon Kindle Unlimited でもNode.jsの本はいっぱいあるんじゃ!!
次回、今回作ったアプリケーション(REST API)をHEROKUにデプロイして本内容は最後となります。
もし、デプロイして多くの方に公開したいと言う方は、ぜひ次回も参考にしてみてください!