はじめに
この記事は、Node.jsとPostgreSQLでシンプルなアプリケーションを作成するシリーズの2回目です。
前回の記事では、Node.jsで作ったシンプルなアプリケーションがBTP上のPostgreSQLに接続できるところまでを確認しました。
この記事のゴール
- Node.jsアプリケーションからPostgreSQLのテーブルに対してCRUD処理を行えるようにする
ステップ
- Read処理(全てのレコード)
- Read処理(指定したidのレコード)
- Create(Insert)処理
- Update処理
- Delete処理
1. Read処理(全てのレコード)
srvフォルダの配下にdb-op.jsというファイルを作成し、ここにCRUD処理を記述します。
プロジェクトの構成は以下のようになります。
node-postgres-sample
└ srv
|- package.json
|- db-conn.js
|- db-op.js
└ server.js
└ mta.yaml
'use strict'
function getAll(db,res) {
const query = 'SELECT * FROM products'
db.manyOrNone(query)
.then((data) => {
// success
res.status(200).json(data)
})
.catch((error) => {
// error
res.status(500)
res.end(`Error accessing DB: ${JSON.stringify(error)}`)
})
}
module.exports = {
getAll: getAll
}
function getAll
はpg-promiseのDatabaseオブジェクトを引数に取ります。DatabaseのmanyOrNoneというメソッドを使い、productsテーブルから全件を取得します。manyOrNoneは複数のレコードが返ってくることが想定されるときに使用します。
server.jsに処理を追加します。/products
というパスが指定されたら、getAllメソッドを呼ぶようにします。
'use strict';
const express = require('express')
const bodyParser = require('body-parser')
const dbConn = require('./db-conn')
const dbOp = require('./db-op') //追加
var _db = undefined
...
app.get('/products', function(req, res) {
dbOp.getAll(_db, res)
})
Cloud Foundryにデプロイ後、/products
のパスを指定するとすべてのレコードが取得できます。
###2. Read処理(指定したidのレコード)
URLで/products/1
のようにidを指定した場合に、指定されたidのレコードを返す処理:getOne
を追加します。
...
function getOne(db, res, id) {
db.one({
name: 'find-product',
text: 'SELECT * FROM products WHERE id = $1',
values : [id]
})
.then((product) => {
res.status(200).json(product)
})
.catch((error) => {
// error
res.status(500)
res.end(`Error accessing DB: ${JSON.stringify(error)}`)
})
}
module.exports = {
getAll: getAll,
getOne: getOne
}
Databaseオブジェクトのoneというメソッドを使い、productsテーブルから指定された1件のレコードを取得します。oneは1件の結果が必ず返ってくることが想定されるときに使用します。
oneに渡しているのはPreparedStatementというオブジェクトです。ここでは以下のパラメータを指定しています。
パラメータ | 意味 |
---|---|
name | 任意の名前。1つのセッション内でユニークである必要がある |
text | クエリストリングまたはQueryFileオブジェクトを指定する。クエリストリングでは、$1 , $2 といった変数を使用することができる |
values | クエリストリングに入る変数を配列で指定する |
server.jsに処理を追加します。/products/:id
というパスが指定されたら、getOneメソッドを呼ぶようにします。
app.get('/products/:id', function(req, res) {
dbOp.getOne(_db, res, req.params.id)
})
デプロイ後、/products/1
を指定すると、指定したレコードのみが返ってきます。
3. Create(Insert)処理
JSON形式で渡したデータをproductsテーブルに登録する処理:insertOne
を追加します。
function insertOne(db, res, newData) {
db.one({
name: 'insert-product',
text: 'INSERT INTO products(name, price) values($1, $2) RETURNING *',
values : [newData.name, newData.price]
})
.then((product) => {
res.status(201).json(product)
})
.catch((error) => {
// error
res.status(500)
res.end(`Error accessing DB: ${JSON.stringify(error)}`)
})
}
module.exports = {
getAll: getAll,
getOne: getOne,
insertOne: insertOne
}
insertOneで呼んでいるのは、getOneと同じDatabaseオブジェクトのoneメソッドです。RETURNINGというのは見慣れない構文ですが、これはINSERT、UPDATE、DELETEの結果更新されたレコードを取得するための命令です。
server.jsに処理を追加します。/products
というパスでPOSTリクエストが来たら、insertOneメソッドを呼ぶようにします。
app.post('/products', function(req, res) {
dbOp.insertOne(_db, res, req.body)
})
この後/products
を見ると、新しいレコードが追加されています。
4. Update処理
productsテーブルを更新する処理:modifyOne
を追加します。
function modifyOne(db, res, id, newData) {
db.one({
name: 'update-product',
text: 'UPDATE products set name = $1, price = $2 WHERE id = $3 RETURNING *',
values : [newData.name, newData.price, id]
})
.then((product) => {
res.status(200).json(product)
})
.catch((error) => {
// error
res.status(500)
res.end(`Error accessing DB: ${JSON.stringify(error)}`)
})
}
module.exports = {
getAll: getAll,
getOne: getOne,
insertOne: insertOne,
modifyOne: modifyOne
}
server.jsに処理を追加します。/products/:id
というパスでPUTリクエストが来たら、modifyOneメソッドを呼ぶようにします。
app.put('/products/:id', function (req, res) {
dbOp.modifyOne(_db, res, req.params.id, req.body)
})
Postmanから"banana"のレコードのpriceを110に変更しました。
5. Delete処理
指定されたレコードをproductsテーブルから削除する処理:deleteOne
を追加します。
function deleteOne(db, res, id) {
db.result({
name: 'delete-product',
text: 'DELETE FROM products WHERE id = $1',
values: [id]
})
.then((product) => {
res.status(200).end('OK')
})
.catch((error) => {
// error
res.status(500)
res.end(`Error accessing DB: ${JSON.stringify(error)}`)
})
}
module.exports = {
getAll: getAll,
getOne: getOne,
insertOne: insertOne,
modifyOne: modifyOne,
deleteOne: deleteOne
}
ここではDatabaseオブジェクトのresultというメソッドを使っています。resultは結果が返ってくることを期待しない場合に使用します。CREATE, UPDATE処理ではRETURNINGで更新されたレコードを取得していましたが、ここでは特に結果を受け取らないのでresultメソッドを使用しています。
server.jsに処理を追加します。/products/:id
というパスでDELETEリクエストが来たら、deleteOneメソッドを呼ぶようにします。
app.delete('/products/:id', function(req, res) {
dbOp.deleteOne(_db, res, req.params.id)
})
最終的なコード
最終的なコードは以下のようになります。
'use strict'
function getAll(db, res) {
const query = 'SELECT * FROM products'
db.manyOrNone(query)
.then((data) => {
// success
res.status(200).json(data)
})
.catch((error) => {
// error
res.status(500)
res.end(`Error accessing DB: ${JSON.stringify(error)}`)
})
}
function getOne(db, res, id) {
db.one({
name: 'find-product',
text: 'SELECT * FROM products WHERE id = $1',
values : [id]
})
.then((product) => {
res.status(200).json(product)
})
.catch((error) => {
// error
res.status(500)
res.end(`Error accessing DB: ${JSON.stringify(error)}`)
})
}
function insertOne(db, res, newData) {
db.one({
name: 'insert-product',
text: 'INSERT INTO products(name, price) values($1, $2) RETURNING *',
values : [newData.name, newData.price]
})
.then((product) => {
res.status(201).json(product)
})
.catch((error) => {
// error
res.status(500)
res.end(`Error accessing DB: ${JSON.stringify(error)}`)
})
}
function modifyOne(db, res, id, newData) {
db.one({
name: 'update-product',
text: 'UPDATE products set name = $1, price = $2 WHERE id = $3 RETURNING *',
values : [newData.name, newData.price, id]
})
.then((product) => {
res.status(200).json(product)
})
.catch((error) => {
// error
res.status(500)
res.end(`Error accessing DB: ${JSON.stringify(error)}`)
})
}
function deleteOne(db, res, id) {
db.result({
name: 'delete-product',
text: 'DELETE FROM products WHERE id = $1',
values: [id]
})
.then((product) => {
res.status(200).end('OK')
})
.catch((error) => {
// error
res.status(500)
res.end(`Error accessing DB: ${JSON.stringify(error)}`)
})
}
module.exports = {
getAll: getAll,
getOne: getOne,
insertOne: insertOne,
modifyOne: modifyOne,
deleteOne: deleteOne
}
'use strict';
const express = require('express')
const bodyParser = require('body-parser')
const dbConn = require('./db-conn')
const dbOp = require('./db-op')
var _db = undefined
const app = express()
app.use(bodyParser.json())
app.get('/', function (req, res) {
res.send('Hello!')
})
app.get('/products', function(req, res) {
dbOp.getAll(_db, res)
})
app.get('/products/:id', function(req, res) {
dbOp.getOne(_db, res, req.params.id)
})
app.post('/products', function(req, res) {
dbOp.insertOne(_db, res, req.body)
})
app.put('/products/:id', function (req, res) {
dbOp.modifyOne(_db, res, req.params.id, req.body)
})
app.delete('/products/:id', function(req, res) {
dbOp.deleteOne(_db, res, req.params.id)
})
function setDBCallback (error, db) {
if (error !== null) {
console.log('error when fetching the DB connection ' + JSON.stringify(error))
return
}
_db = db;
}
var PORT = process.env.PORT || 8088
var server = app.listen(PORT, function() {
const host = server.address().address
const port = server.address().port
console.log(`Example app listening at http://${server}:${port}`)
dbConn.getDB(setDBCallback);
})
まとめ
この記事では、以下を実施しました。
- Node.jsアプリケーションからPostgreSQLのテーブルに対してCRUD処理を行えるようにする
次回はXSUAAサービスを使って認証を追加したいと思います。