Node.jsを使用してPostgreSQLにアクセスしデータのリレーションを行う方法

Node.jsでPostgreSQLを使う

はじめに

今回はオープンソースソフトウェアでリレーショナルデータベース管理システム(RDMS)であるPostgreSQLにNode.jsからアクセスし、データを取得したり、追加したりしてみたいと思います。SQLとはStructured Query Languageの略語です。PostgreSQLの概要は省略しますが気になる方はこちらを参照して下さい。PostgreSQLの超入門。

Node.jsを使用してPostgreSQLにアクセスしデータのリレーションを行う方法はいくつかあります。今回はmacにPostgreSQLをインストールし、pgAdmin4というGUIで操作できるツール(MySQLのphpmyAdminの様な視覚的ツール)を使って進めてみたいと思います。

この様な方々を対象としています。

  • Node.jsからPostgreSQLを操作したい
  • まずはSTEP BY STEPでpostgreSQLの扱いを覚えたい
  • 直接queryを扱いたい
  • GUIを使って操作したい
  • macをお使いの方

実行環境

macOS (Catalina) version 10.15.2

macbook Pro(15-inch, 2017)

brew –version : 2.2.2

nodebrew use (current: 12.13.1)

npm version (6.12.1)

Installation

では実行環境から始めていきたいと思います。

PostgreSQL 本家(英語)の Mac OS X 用のパッケージ一覧サイトから11.6のバージョンを選びました。理由はありませんが最新はさけました。そのままインストールを進んで、pgAdmin4もインストールしましょう。今回は使用していませんがHomebrewを使ったインストール方法もあるのでお好きなほうでインストールしてみて下さい。インストール方法を詳しく解説しているサイトを参考にしました。postgreSQLのインストール。インストール時にパスワードを設定します。このパスワードはアクセスする時に必要ですので、必ず控えておきましょう。

pgAdmin4の写真

pgAdmin4を使ってdatabase,tableを作成

インストールが完了しましたらアプリケーションからpgAdmin4を起動して下さい。以下のアイコンをクリックすることで起動します。

databaseの作成

ではdatabaseを作成します。

Serversをクリックし、databasesを右クリックします。

作成ダイアログにdatabase名をつけて、必要ならownerを変更します。今回下記の画像の様に設定しました。これでdatabaseが作成されたことになります。

tableの作成

次にtableを作成します。

下記の画像の様に作成したdatabaseをクリックしてschemasをクリックして下さい。tablesで右クリックし、tableを作成します。

tableからcreateで、以下の様にダイアログがでます。

ここでtableとカラムを作成します。今回はNameusersとし、ownerもそのままで、Columnsをクリックします。

Columnsのプラスボタンをクリックするとカラムを作成・追加することができます。

カラムの作成

今回はidnameの二種類のカラムを作成します。

データタイプやprimary keyを設定し、カラムを作成してみましょう。下記の様に設定して下さい。設定したらsaveで終了します。

これでdatabaseとtableとカラムを作成できました

ここからはファイルを作成し、コードによってデータベースにアクセスしたり、データを挿入したりとやっていきましょう。

Projectの作成

手順

  • package.jsonを作成 (npm init)
  • index.jsを作成
  • postgres モジュールをインストール(npm i –save pg)

package.jsonの作成

任意の場所にフォルダーを作成し、エディターを立ち上げます。ターミナルを使って作成したフォルダーにパッケージジェイソンを生成します。コマンドはnpm initです。

$ npm init

npmとはnode package managerというモジュールの管理システムであり、多くのJavaScriptフレームワークを使う際に必須になります。

{
  "name": "test-posgres",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
 },

index.jsの作成

package.jsonがフォルダーに生成されたら、scriptsの記述を変更していきますが、まずはindex.jsをルートディレクトリ に作成します。index.jsはpackage.jsonに記述されているmainのindex.jsです。index.jsの作成後以下の様に記述して下さい。

console.log('Hello')

そしてscripts内に記述されているtestを変更して、index.jsをサーバー起動時に読みこむ様に変更してみます。

npm scriptsの変更

下記の様にscriptsのtestを書き換えます。この様にすることでnpm run testコマンドでindex.jsを読み込みます。npm runの記述は必要ありません。

{
  "name": "express.test-postgres",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
   // 変更
    "test": "node index.js"
  },
  "author": "",
  "license": "ISC"
}
$ npm run test
> node index.js

hello

postgres モジュールをインストール

postgresをインストールします。

$ npm i --save pg

これでprojectが作成されました。次から実際にコードを記述していきましょう。

PostgreSQLにアクセスする

const { Client } = require('pg')
const client = new Client ({
  user: "postgres",
  password: "********",
  host: "localhost",
  port: "5432",
  database: "postgres-users"
})

client.connect()
.then(() => console.log("Connected successfuly"))
.then(() => client.query("select * from users"))
.then(results => console.table(results.rows))
.catch((e => console.log(e)))
.finally((() => client.end()))
$ npm run test

上記のコードによりコンソールでサーバーを起動します。

> node index.js

Connected successfuly
┌─────────┐
│ (index) │
├─────────┤
└─────────┘

成功しました。

データベースのテーブルが表示されました。下記のコードはpostgresであるpgからClientを呼び出し、インスタンスの作成を行っています。この様にすることでデータベースを特定します。

const { Client } = require('pg')
const client = new Client ({
  user: "postgres",
  password: "********",
  host: "localhost",
  port: "5432",
  database: "postgres-users"
})

下記のコードではpromiseベースのコードで記述しています。

client.connect()で接続し、client.queryでquery操作、select 文でusersテーブルを取得、console.tableで結果を表示、thenやcatchやfinallyはJavaScriptのpromiseの記述になります。

client.connect()
// 接続を確認
.then(() => console.log("Connected successfuly"))
// users テーブルから取得
.then(() => client.query("select * from users"))
// 結果を返す
.then(results => console.table(results.rows))
// エラーの場合
.catch((e => console.log(e)))
// 終了
.finally((() => client.end()))

insertを使ってデータを追加する

postgreSQLにアクセスすることができました。次にデータを追加してみましょう。データを追加するためにはinsertを使います。無事に追加することが出来れば、データを取得します。先ほどと同じ様にpromiseを使用しますが、ファイルを分けて記述したいと思います。

table.jsを作成

rootディレクトリ にファイルを作成します。それからindex.jsのコードをコピーして貼り付けましょう。そして一行を追加してください。

const { Client } = require('pg')
const client = new Client ({
  user: "postgres",
  password: "********",
  host: "localhost",
  port: "5432",
  database: "postgres-users"
})

client.connect()
.then(() => console.log("Connected successfuly"))
// 追加するのはこの列のコード
.then(() => client.query("insert into users values ($1, $2)", [001, "kenny"]))
.then(() => client.query("select * from users"))
.then(results => console.table(results.rows))
.catch((e => console.log(e)))
.finally((() => client.end()))

npm scriptsの変更

{
  "name": "express.test-postgres",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "node index.js",
   // 追加する
    "table": "node table.js"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "pg": "^7.17.0"
  }
}

npm scriptを追記することでtable.jsをサーバーから起動することができます。ターミナルでコマンドを打ち込んでください。

$ npm run table

データを追加し、取得できました。

> node table.js

Connected successfuly
┌─────────┬────┬─────────┐
│ (index) │ id │  name   │
├─────────┼────┼─────────┤
│    0    │ 1  │ 'kenny' │
└─────────┴────┴─────────┘

Pg Admin4で確認

もう一度コマンドでサーバーを起動してもエラーが返されます。

なぜならデータの重複を許可していないからです。pgAdmin4を開いて確認してみましょう。Schemasのusersで右クリックしてView/Edit Data => All Rowsを選択してください。

下の方に追加したデータが参照できるのがわかります。確かにデータが追加できていますが、同じデータを追加できない様にしているのはPrimary Keyを許可していないからです。

カラムを登録する際にidのPrimary KeyをYesに変更しました。

結果、重複したidをブロックしていることがわかります。

async awaitを使ってpostgreSQLにアクセスする

次はasync awaitを使いましょう。

promiseと同じ非同期処理ですが、同期処理の様に記述することができるので、使いやすいprimiseです。例の様に新たにasyncTable.jsを作成しnpm scriptに記述しましょう。

.

npm scriptsにasyncTable.jsを追加する

{
  "name": "express.test-postgres-cli",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "node index.js",
    "table": "node table.js",
    // 追加
    "asyncTable": "node asyncTable.js"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "pg": "^7.17.0"
  }
}

postgreSQLにasync awaitでアクセスする

functionの前にasyncを配置し、非同期処理したい処理にawaitを配置します。

この場合、エラーの場合の処理を入れていなければ、警告やエラーとなる場合が多いので、try と catch、finallyを使って処理を分けて記述します。

const { Client } = require('pg')
const client = new Client({
  user: "postgres",
  password: "********",
  host: "localhost",
  port: "5432",
  database: "postgres-users"
})


async function asyncTable() {
  try {
    await client.connect()
      console.log('Connected successfully in async')
      const results = await client.query("select * from users")
      console.table(results.rows)
      client.end()
      console.log("Client disconnected successfully")
  }
  catch (ex) {
    console.log(`Something wrong happend ${ex}`)
  }
   finally {
     await client.end()
     console.log('Client disconnected successfully')
   }
}
asyncTable()

処理を分けると、tryの処理は成功した時の処理、catchは失敗した時の処理です。単純にコンソールに出力しているだけですがアプリケーションの場合、何らかのエラーハンドリングをすることになります。

awaitの処理は、client.connect()とusersテーブルを取得する処理としています。エラーが起こらなければ処理はfinallyに移って、client.end()の後にコンソールに出力されて終了となります。

結果をコンソールで確認します。

$ npm run asyncTable

以下の様にコンソールに出力されれば、成功です。

> node asyncTable.js

Connected successfully in async
┌─────────┬────┬─────────┐
│ (index) │ id │  name   │
├─────────┼────┼─────────┤
│    0    │ 1  │ 'kenny' │
└─────────┴────┴─────────┘
Client disconnected successfully
Client disconnected successfully

トランザクションを使ってqueryを操作する

async awaitでqueryを操作できました。

次は簡単なトランザクションにトライしてみましょう。トランザクションとは、複数の手順を単一の「すべてか無しか」の操作にまとめあげることです。行われるべき処理のある一部分のみが成功であったり、失敗であったりと、データベースに保存されるべき情報の整合性が取れないとなることは避けるべきです。

トランザクションを使用すれば、複数の処理をまとめあげ、ある処理が行われるまではdbに保存されないといったものになります。

BEGINとCOMMIT

BEGINとCOMMITの間の処理をひとまとめに処理します。COMMITが実行されるまでが一連の流れとなります。

少し込み入ってきましたので、一度ファイルを作成して、コードを記述して確認していきましょう。

transaction.jsを作成し、npm scriptsに記述する

{
  "name": "express.test-postgres",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "node index.js",
    "table": "node table.js",
    "asyncTable": "node asyncTable.js",
   // 追加
    "transaction": "node transaction.js"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "pg": "^7.17.0"
  }
}

BEGINとCOMMITを使ったトランザクション

transaction.jsに記述します。

const { Client } = require('pg')

const client = new Client({
  user: "postgres",
  password: "********",
  host: "localhost",
  port: "5432",
  database: "postgres-users"
})

async function transaction() {
  try {
    await client.connect()
   // BEGIN
    await client.query("BEGIN")
    await client.query("select * from users")
    await client.query("insert into users values ($1, $2)", ['001', 'hannna'])
    console.log('insert a new row')
   // COMMIT
    await client.query("COMMIT")
  }
  catch(error) {
    console.log(error)
  }
  finally {
    await client.end()
    console.log('Client disconnected successfully')
  }
}

transaction()

上記のコードのasync await transaction関数をみていきます。

tryの処理ではclient.connect()で接続し、client.queryの引数でBEGIN、その後client.queryでCOMMITする記述となっています。BEGINでトランザクションが始まり、COMMITが実行されるまではデータベースにデータを保存、更新しない処理となり、間の処理は全て成功するか全て失敗するかの結果となります。

今回上記の処理の場合、間の処理はusersテーブルにアクセスし、id001のhannnaをdbに追加する処理となっています。idが001なのはkennyとしてすでに追加されているのでこの処理はエラーとなります。idが重複しているので、トランザクション内の処理は全てエラーとなります。

次はCOMMITを削除し、id002のhannnaを追加して確認してみます。

async function transaction() {
  try {
    await client.connect()
    await client.query("BEGIN")
   // 何らかの処理  
   //何らかの処理
    await client.query("COMMIT")
  }
  catch(error) {
    console.log(error)
  }
  finally {
    await client.end()
    console.log('Client disconnected successfully')
  }
}
const { Client } = require('pg')

const client = new Client({
  user: "postgres",
  password: "********",
  host: "localhost",
  port: "5432",
  database: "postgres-users"
})

async function transaction() {
  try {
    await client.connect()
    await client.query("BEGIN")
    await client.query("select * from users")
    //変更した行
    await client.query("insert into users values ($1, $2)", ['002', 'hannna'])
    console.log('insert a new row')
   // コメントアウト
    //await client.query("COMMIT")
  }
  catch(ex) {
    console.log('Failed to execute something' + ex + "this is error")
    console.log(`Failed to execute something ${ex}`)
    console.log(ex)
  }
  finally {
    await client.end()
    console.log('Client disconnected successfully')
  }
}

transaction()

データベースに保存したいデータをid002,hannnaと設定し直し、COMMIT文をコメントアウトしてもう一度コマンドします。

$ npm run transaction
> node transaction.js

insert a new row
Client disconnected successfully

上記の様な結果となりました。コンソールにはinsert a new rowと出力されています。

ではpgAdmin4で確認してみましょう。ツールバーの雷のアイコンをクリックして更新してください。

何度更新してもhannnaは追加されません。

これはBEGINがトランザクションのトリガーとなるも、COMMITが存在しないためトランザクションが完了できません。ではCOMMITのコメントアウトを外してもう一度トライしてみましょう。コンソールでコマンドします。

$ npm run transaction
> node transaction.js

insert a new row
Client disconnected successfully

ではpgAdmin4で確認してみましょう。ツールバーの雷のアイコンをクリックして更新してください。

次は成功しました。

この様にBEGINとCOMMITの間の処理を確実に行うための仕組みがトランザクション機能の一つになります。また一つでもエラーが発生すれば、トランザクション内の処理は全てエラーとなり、整合性が保たれるということになります。

まとめ

今回はnode.jsからpostgreSQLにアクセスし、query操作を行いました。

dbに正しいデータを保存するための仕組みの基本を学習することで理解をより深めることができました。直接queryを触ることのないORMモジュールであるsequelizeなどありますが、基本的なinsert文やselect文も重要です。まだまだいろんな機能にトライしていきましょう。

SNSでもご購読できます。

検索

コメントを残す

*