LoginSignup
1
0

More than 1 year has passed since last update.

【BTP】Node.jsとPostgreSQLでシンプルなアプリケーションを作成(2)CRUD処理編

Last updated at Posted at 2021-05-28

はじめに

この記事は、Node.jsとPostgreSQLでシンプルなアプリケーションを作成するシリーズの2回目です。
前回の記事では、Node.jsで作ったシンプルなアプリケーションがBTP上のPostgreSQLに接続できるところまでを確認しました。

この記事のゴール

  • Node.jsアプリケーションからPostgreSQLのテーブルに対してCRUD処理を行えるようにする

ステップ

  1. Read処理(全てのレコード)
  2. Read処理(指定したidのレコード)
  3. Create(Insert)処理
  4. Update処理
  5. Delete処理

1. Read処理(全てのレコード)

srvフォルダの配下にdb-op.jsというファイルを作成し、ここにCRUD処理を記述します。
プロジェクトの構成は以下のようになります。

node-postgres-sample
 └ srv
     |- package.json
     |- db-conn.js
     |- db-op.js
     └  server.js
 └ mta.yaml
srv/db-op.js
'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メソッドを呼ぶようにします。

srv/server.js
'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のパスを指定するとすべてのレコードが取得できます。
image.png

2. Read処理(指定したidのレコード)

URLで/products/1のようにidを指定した場合に、指定されたidのレコードを返す処理:getOneを追加します。

srv/db-op.js
...

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メソッドを呼ぶようにします。

srv/server.js
app.get('/products/:id', function(req, res) {
    dbOp.getOne(_db, res, req.params.id)
})

デプロイ後、/products/1を指定すると、指定したレコードのみが返ってきます。
image.png

存在しないidを指定した場合はエラーになります。
image.png

3. Create(Insert)処理

JSON形式で渡したデータをproductsテーブルに登録する処理:insertOneを追加します。

srv/db-op.js
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メソッドを呼ぶようにします。

srv/server.js
app.post('/products', function(req, res) {
    dbOp.insertOne(_db, res, req.body)
})

Postmanでテストします。
image.png

この後/productsを見ると、新しいレコードが追加されています。
image.png

4. Update処理

productsテーブルを更新する処理:modifyOneを追加します。

srv/db-op.js
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メソッドを呼ぶようにします。

srv/server.js
app.put('/products/:id', function (req, res) {
    dbOp.modifyOne(_db, res, req.params.id, req.body)
})

Postmanから"banana"のレコードのpriceを110に変更しました。
image.png

5. Delete処理

指定されたレコードをproductsテーブルから削除する処理:deleteOneを追加します。

srv/db-op.js
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メソッドを呼ぶようにします。

srv/server.js
app.delete('/products/:id', function(req, res) {
    dbOp.deleteOne(_db, res, req.params.id)
})

削除に成功すると、'OK'が返ってきます。
image.png

一覧を見るとレコードが削除されたことが確認できます。
image.png

最終的なコード

最終的なコードは以下のようになります。

srv/db-op.js
'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
}
srv/server.js
'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サービスを使って認証を追加したいと思います。

参考

1
0
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
0