前提
お世話になります。
node.jsで制作しているメモアプリで、現在MySQLからPostgreSQLへの移行を試みているのですが、そこで発生した問題についてお尋ねさせてください。
viewファイルにはEJSファイルを使用しております(EJSというのは、HTML内にJavascriptを簡単に記述できるJSのテンプレートエンジンです)。
先日投稿いたしました投稿( https://teratail.com/questions/6zqnuiwft1zlkp )の続きで、実行時に起こるエラーが変わりましたので、このたび質問を投稿し直すこととなりました。
見ていただかなくても今回の質問に差し障りはないと思いますが、以前発生していたエラーをご覧いただく際は、お手数ですが上記のページをご参照いただけますと幸いです。
メモアプリのルーティングについて簡単に説明しますと、/indexでメモの一覧情報を取得し/trashでゴミ箱に入れたメモの一覧ページを取得、また/newで新規メモの作成、/edit/:idで任意のメモの編集が行えるという感じです。
発生している問題
app.jsにて、PostgreSQLのテーブルデータをdata.rowsという形でEJSファイルに渡しているのですが、実際にアプリを実行すると次のような流れでエラーが発生します。
まず、アプリを実行して最初に'localhost:8001/index'、または'localhost:8001/trash'にアクセスすると、問題なくページを読み込むことができます。
しかし、そこから違うページを取得しようとすると、下記の出力結果における14行目以降のようなエラーが起こり、接続が途絶えてしまいます。
app.jsの出力結果
1Listeninng on 8001 // 1行目: アプリ実行時にconsole.infoで出力されるテキスト 2[ // 2~12行目: アプリを実行後、初めて/indexもしくは/trashにアクセスした際にconsole.logで出力されるdata.rowsの内容(/indexの場合) 3 { 4 id: 1, 5 title: 'hoge', 6 content: 'huga', 7 created: 2023-12-01T07:16:14.517Z, 8 modified: 2023-12-06T07:32:17.110Z, 9 is_removed: false, 10 is_locked: false, 11 modified_stamp: 'Dec 06 23 16:32' 12 } 13] 14C:\Users\Kanon\workspace\memo-app\app.js:79 // 14行目~: 最初にアクセスしたページから別のページに飛ぼうとすると起こるエラー 15 console.log(data.rows); 16 ^ 17 18TypeError: Cannot read properties of undefined (reading 'rows') 19 at Query.callback (C:\Users\Kanon\workspace\memo-app\app-new.js:79:21) 20 at Query.handleError (C:\Users\Kanon\workspace\memo-app\node_modules\pg\lib\query.js:128:19) 21 at C:\Users\Kanon\workspace\memo-app\node_modules\pg\lib\client.js:580:15 22 at processTicksAndRejections (node:internal/process/task_queues:78:11)
/indexに最初にアクセスするとそこから/trashにアクセスする際にエラーが起こり、/trashに最初にアクセスすると逆の結果になります。
/newにはいつでもアクセスできるのですが、新規メモのデータを保存しようとするとエラーが起こりました。
/edit/:idに至っては、最初のアクセスからエラーが起こり、常に取得ができない状態です。
なお、どのケースでも発生するエラーメッセージに変わりはありませんでした。
該当のソースコード・テーブル
こちらが該当のテーブル情報です。
PostgreSQLのmemosテーブル
1memo_app=# \d memos 2 テーブル"public.memos" 3 列 | タイプ | 照合順序 | Null 値を許容 | デフォルト 4----------------+-----------------------------+----------+---------------+------------------------------------------------------- 5 id | integer | | not null | nextval('memos_id_seq'::regclass) 6 title | character varying(40) | | | 'Untitled'::character varying 7 content | character varying(10485760) | | | 8 created | timestamp without time zone | | not null | CURRENT_TIMESTAMP 9 modified | timestamp without time zone | | not null | CURRENT_TIMESTAMP 10 is_removed | boolean | | not null | false 11 is_locked | boolean | | not null | false 12 modified_stamp | character varying(15) | | | generated always as (custom_to_char(modified)) stored 13インデックス: 14 "memos_pkey" PRIMARY KEY, btree (id) 15トリガー: 16 update_tri BEFORE UPDATE ON memos FOR EACH ROW EXECUTE FUNCTION set_update_time()
また、該当のJSファイル(app.jsとmodule.js)はこちらです。
app.js(周辺情報のみ)
1const express = require('express'); 2const mod = require('./module') 3const app = express(); 4 5app.use(express.static('public')); 6app.use(express.urlencoded({extended: false})); 7 8// Memo Listの取得 9app.get('/index', (req, res) => { 10 const connection = mod.connection; 11 connection.query( 12 'SELECT * FROM memos WHERE is_removed = false', 13 (err, data) => { 14 const result = data.rows; 15 console.log(result); 16 res.render('index.ejs', {memos: result}); 17 connection.end(); 18 } 19 ); 20}); 21 22// 新規メモの作成ページを表示 23app.get('/new', (req, res) => { 24 res.render('new.ejs'); 25}); 26 27// 新規メモを保存 28app.post('/create', (req, res) => { 29 const connection = mod.connection; 30 connection.query( 31 'INSERT INTO memos (title, content) VALUES (?, ?)', 32 [req.body.memoTitle, req.body.memoContent], 33 (err, data) => { 34 res.redirect('/index'); 35 connection.end(); 36 } 37 ); 38}); 39 40// メモの編集ページを取得 41app.get('/edit/:id', (req, res) => { 42 const connection = mod.connection; 43 connection.query( 44 'SELECT * FROM memos WHERE id = ?', 45 [req.params.id], 46 (err, data) => { 47 const result = data.rows; 48 res.render('edit.ejs', {memo: result[0]}); 49 connection.end(); 50 } 51 ); 52}); 53 54// メモを更新 55app.post('/update/:id', (req, res) => { 56 const connection = mod.connection; 57 connection.query( 58 'UPDATE memos SET title = ?, content= ? WHERE id = ?', 59 [req.body.memoTitle, req.body.memoContent, req.params.id], 60 (err, data) => { 61 res.redirect('/index'); 62 connection.end(); 63 } 64 ); 65}); 66 67// Trash Listの取得 68app.get('/trash', (req, res) => { 69 const connection = mod.connection; 70 connection.query( 71 'SELECT * FROM memos WHERE is_removed = true', 72 (err, data) => { 73 const result = data.rows; 74 console.log(result); 75 res.render('trash.ejs', {memos: result}); 76 connection.end(); 77 } 78 ); 79});
module.js(データベースの接続情報)
1const { Client } = require('pg'); 2 3const connection = new Client({ 4 user: 'postgres', 5 host: 'localhost', 6 database: 'hoge', 7 password: 'huga', 8 port: 5432, 9}); 10 11connection.connect(); 12 13module.exports = { 14 connection 15}
試したこと
エラー文を検索したところ、'TypeError: Cannot read properties of undefined (reading 'hoge')'というエラーはその値がundefinedの時に起こるという情報が見つかりました。
しかし、rows配列は存在しており、実際に一度目のアクセスでは取得が可能なため、解決に紐付けることはできませんでした。
参照したのは以下のページです。
https://magazine.techacademy.jp/magazine/26836
また、以下のような英語の記事をいくつか参照してみたのですが、今回の問題とは原因が少し異なるようでした(恥ずかしながら英語にあまり強くないため、正しく読み取れていない可能性は大いにあります)。
https://discourse.metabase.com/t/cannot-read-properties-of-undefined-reading-rows/18166
https://github.com/TanStack/table/issues/5098
他に、data.rowsを変数に代入してからEJSファイルに渡したりもしたのですが、特に関係はなかったようです。
追記(2023/12/07)
/edit:idのページの取得時にエラーが出る問題については、取得したテーブルデータを第二引数に指定する際のクエリ記法がMySQLとPostgresで異なっていることが原因でした。
/edit:id以外のルーティングでも使用していた記法ですので、全て以下のように修正することで問題なくデータを取得できるようになりました。
app.js(以下は/edit/:idのルーティングの例)
1// MySQLの記法 2app.get('/edit/:id', (req, res) => { 3 pool.query( 4 'SELECT * FROM memos WHERE id = $1', // '?'という書き方はPostgreSQLに対応していない 5 [req.params.id], 6 (error, results) => { 7 res.render('edit.ejs', {memo: results.rows[0]}); 8 } 9 ); 10}); 11 12// PostgreSQLの記法 13app.get('/edit/:id', (req, res) => { 14 pool.query( 15 'SELECT * FROM memos WHERE id = $1', // '?'の部分をPostgreSQLの記法に合わせ'$1'という書き方に変更 16 [req.params.id], 17 (error, results) => { 18 res.render('edit.ejs', {memo: results.rows[0]}); 19 } 20 ); 21});
参照したページは以下です。
https://qiita.com/sa9ra4ma/items/765660dfd6936aeaff1b
また、文字数の上限に達したため、今回の問題とは関係がなかったviewsファイルの情報を削除しました。
補足情報(FW/ツールのバージョンなど)
実行環境はWindows11です。
Node: v16.14.2
MySQL: v8.0.3
PostgreSQL: v16.1
他にご入用の情報があれば仰ってくださいませ。
知識が浅くて恐縮ですが、どうぞよろしくお願いいたします。
あなたの回答
tips
プレビュー