Node.jsにて、PostgreSQLのデータを上手くEJSファイルに渡せません。

前提

お世話になります。

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}

以下はviewファイルの情報です。
index.ejs(メモ一覧の取得)とtrash.ejs(ゴミ箱に移動させたメモ一覧の取得)は構造が同じです。
また、edit.ejs(既存メモの編集)も、memosテーブルから情報を取得している部分以外はnew.ejs(新規メモの作成)と同じ作りになっております。

index.ejs(trash.ejsと構造が同じ)

1<!DOCTYPE html> 2<html lang="en"> 3<head> 4 <meta charset="UTF-8"> 5 <meta http-equiv="X-UA-Compatible" content="IE=edge"> 6 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 7 <title>Kanon's Memo</title> 8 <link rel="icon" href="/images/black-cat.png"> 9 <link rel="stylesheet" href="/css/style.css"> 10</head> 11<body> 12 <%- include('header'); %> 13 <main> 14 <div class="index-container"> 15 <div class="container-header"> 16 <div class="container-title"> 17 <h1>Memo List</h1> 18 <img src="/images/black-cat.png" alt="S-cat" class="cat-image"> 19 </div> 20 <div class="button"> 21 <a href="/trash"class="optional-button move-button">Trash</a> 22 <a href="/new" class="optional-button">+ New</a> 23 </div> 24 </div> 25 <div class="index-table-wrapper"> 26 <div class="table-head"> 27 <span class="title-column">Name</span> 28 <span class="modified-column">Last Modified</span> 29 </div> 30 <ul class="table-body"> 31 <% memos.forEach((memo) => { %> 32 <li> 33 <span class="title-column"><%= memo.title %></span> 34 <div class="items"> 35 <span class="modified-column"><%= memo.modified %></span> 36 <div class="icon-area"> 37 <div class="menu-icon"> 38 <div></div> 39 <div></div> 40 <div></div> 41 </div> 42 <ul class="menu-content"> 43 <li><a href="/edit/<%= memo.id %>">Edit</a></li> 44 <li><form action="/remove/<%= memo.id %>" method="post"> 45 <input type="submit" value="Remove"> 46 </form></li> 47 </ul> 48 </div> 49 </div> 50 </li> 51 <% }); %> 52 </ul> 53 </div> 54 </div> 55 <div class="footer"> 56 <small class="copy-right">&copy; 2023 kanon</small> 57 </div> 58 </main> 59</body> 60</html>

edit.ejs(memosのデータを渡している部分以外はnew.ejsと構造が同じ)

1<!DOCTYPE html> 2<html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta http-equiv="X-UA-Compatible" content="IE=edge"> 6 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 7 <title>Kanon's Memo</title> 8 <link rel="icon" href="/images/black-cat.png"> 9 <link rel="stylesheet" href="/css/style.css"> 10 <script src="//ajax.googleapis.com/ajax/libs/jquery/2.0.2/jquery.min.js"></script> 11 </head> 12<body> 13 <%- include('header'); %> 14 <main> 15 <div class="new-container"> 16 <form action="/update/<%=memo.id%>" method="post" class="submit"> 17 <div class="memo-wrapper"> 18 <input value="<%= memo.title %>" class="title-area input" name="memoTitle" type="text" placeholder="Title"> 19 <input type="text" name="dummy" style="display: none;"> 20 <textarea class="content-area input" name="memoContent" placeholder="Text"><%= memo.content %></textarea> 21 </div> 22 <div class="menu-wrapper"> 23 <input type="button" onclick="submit();" class="save-button" value="Save"> 24 <a href="/index" class="cancel-button">Cancel</a> 25 </div> 26 </form> 27 </div> 28 </main> 29 <script> 30 $(function($) { 31 $('.input').keydown(function(e) { 32 if(event.ctrlKey) { 33 $('.submit').submit(); 34 return false; 35 if (e.keyCode === 13 && $(this).val()) { 36 } 37 } 38 }); 39 }); 40 </script> 41</body> 42</html>

試したこと

エラー文を検索したところ、'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ファイルに渡したりもしたのですが、特に関係はなかったようです。

補足情報(FW/ツールのバージョンなど)

実行環境はWindows11です。

Node: v16.14.2
MySQL: v8.0.3
PostgreSQL: v16.1

他にご入用の情報があれば仰ってくださいませ。
知識が浅くて恐縮ですが、どうぞよろしくお願いいたします。

コメントを投稿

0 コメント