🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
MySQL

MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

Node.js

Node.jsとはGoogleのV8 JavaScriptエンジンを使用しているサーバーサイドのイベント駆動型プログラムです。

Express

ExpressはNode.jsのWebアプリケーションフレームワークです。 マルチページを構築するための機能セットおよびハイブリッドのWebアプリケーションを提供します。

Q&A

解決済

1回答

1543閲覧

node.js + express + MySQLで undefined is not function と怒られた

HYDESA

総合スコア8

MySQL

MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

Node.js

Node.jsとはGoogleのV8 JavaScriptエンジンを使用しているサーバーサイドのイベント駆動型プログラムです。

Express

ExpressはNode.jsのWebアプリケーションフレームワークです。 マルチページを構築するための機能セットおよびハイブリッドのWebアプリケーションを提供します。

0グッド

0クリップ

投稿2021/01/28 08:22

編集2021/01/29 07:44

前提・実現したいこと

現在、expressappを使って
当月と前月以前のデータと動画の表示の処理をし、
またHTML側の月の遷移により
postget で同じSQLを動かしたいため、
app.use'/*'にて共通処理としました。

ところがリダイレクトにより app.use('/*')
複数回処理されていることがわかりました。

この事から、SQLを無駄に動かさないように
app.useの共通処理をやめ、
get post ともに1度だけSQLを実行すべく
functionでモジュール化しましたが、
知識が乏しいので、undefined is not a function
怒られました。

どのようにモジュール化すればよいか、ご教示いただけると嬉しいです。

発生している問題・エラーメッセージ

express

1const express = require('express'); 2const app = express() 3const mysql = require('mysql2/promise'); 4// ~省略~ 5const ddate = new Date();// 今日を取得 6var yymm = ddate.toFormat("YYMM"); // 2101形式でセット 7 8function getColumnData(num, result, column){ 9// ここで連想配列から配列にセットし直してreturn 10} 11// ここにfunction getSQLData(sql)を設置 12 13app.set('ejs', ejs.renderFile); 14const pool = mysql.createPool({ 15 host: 'localhost', 16 user: 'root', 17 password: 'test', 18 database: 'test', 19}); 20app.use('/', express.static(path.join(__dirname, 'stream')));// Movie 21app.use('/', express.static(path.join(__dirname, 'image')));// mp4ファイルのサムネイル用 22app.post('/views/', async(request, response, next) => { 23 yymm = ${request.body.test}`; // postで年月情報取得 24 next(); 25}); 26app.get('/', ,async( request, response, next ) => { 27 moveImage();// 取得写真を別フォルダに移動 28 makeMovie();// 写真から動画生成 exec(`ffmpeg ... 29 console.log("app.get"); 30 next(); 31}); 32app.use('/*',async( request, response ) => { 33 try{ 34 // SQLによるDBデータ取得 35 sql = `select * from test where yymm = '${yymm}'`; 36 var [data] = await pool.query(Sql); 37 // var data = await getSQLData(sql); とセット 38 // ここでは1個ですが、実際には5個のSQLを動かしていて 39 // getColumnDataモジュールで配列に置き換えた処理を4個動かしています 40 response.render('test.ejs',{ 41 data: data, 42 item: item, 43 ... 44 }); 45 } 46 catch(err){ 47 console.log(`catch: ${err}`); 48 }; 49 console.log("Successful"); 50}); 51 52https.createServer(options, app).listen(port,() => { 53 console.log('server start'); 54});

javascript

1// console 2 3node test.js 4app.get 5server start 6Successful 7Successful

やった事

express

1function getSQLData(sql){ 2 var [data] = pool.query(sql); 3 return data; 4}

また、こうした方がいいんじゃない?という上記ソースを全く変えてしまう
お答えでも、全然かまいません。
すいませんが、よろしくお願い致します。

解決後の備忘録

今回の変更箇所を記載しておきます。
function から const 変数名 に変更。

express

1const getSQLData = async()=>{ 2 sql = `select * from test where yymm = '${yymm}'`; 3 // グローバル変数にセット 4 var [Gl_Data] = await pool.query(Sql); 5 ... 6};

変更後がこちら↓

express

1const express = require('express'); 2const app = express() 3const mysql = require('mysql2/promise'); 4// グローバル宣言 5var tmpData; 6var gl_Data; 7var gl_Item; 8... 9// ~省略~ 10const ddate = new Date();// 今日を取得 11var yymm = ddate.toFormat("YYMM"); // 2101形式でセット 12 13function getColumnData(num, result, column){ 14// ここで連想配列から配列にセットし直してreturn 15} 16// SQLの実行関数 17const getSQLData = async()=>{ 18 // SQLによるDBデータ取得 19 sql = `select * from test where yymm = '${yymm}'`; 20 var [Gl_Data] = await pool.query(Sql); 21 ... 22}; 23 24app.set('ejs', ejs.renderFile); 25const pool = mysql.createPool({ 26 host: 'localhost', 27 user: 'root', 28 password: 'test', 29 database: 'test', 30}); 31app.use('/', express.static(path.join(__dirname, 'stream')));// Movie 32app.use('/', express.static(path.join(__dirname, 'image')));// mp4ファイルのサムネイル用 33app.post('/views/', async(request, response, next) => { 34 yymm = ${request.body.test}`; // postで年月情報取得 35 // SQLによるDBデータ取得 36 await getSQLData(); 37 next(); 38}); 39app.get('/', async( request, response, next ) => { 40 moveImage();// 取得写真を別フォルダに移動 41 makeMovie();// 写真から動画生成 exec(`ffmpeg ... 42 console.log("app.get"); 43 44 // SQLによるDBデータ取得 45 await getSQLData(); 46 next(); 47}); 48app.use('/*',async( request, response, next ) => { 49 try{ 50 // getColumnDataモジュールで配列に置き換えた処理を4個動かしています 51 var data = getColumnData(hogeNum, gl_Data, 'data');// 配列に置き換え 52 var item = getColumnData(hogeNum, gl_Item, 'item');// 配列に置き換え 53 response.render('test.ejs',{ 54 data: data, 55 item: item, 56 ... 57 }); 58 } 59 .catch(err){ 60 // html側にエラー状況を通告 61 response.status(404).send('404 error, Page Not found'); 62 // コンソール側にエラーを表示 63 console.log(`catch: ${err}`); 64 }; 65 console.log("Successful"); 66}); 67 68https.createServer(options, app).listen(port,() => { 69 console.log('server start'); 70});

うん、スッキリしました。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

undefined is not function

まずこのエラーそのものに関して回答します。
JSに於いて「値()」という風に値の後に丸括弧()を付けると、
値を関数と認識して実行しようとします。

()は関数実行の証と考えてください。

しかし、関数実行が出来るのは関数だけ。
undefined is not functionと叱られるのはすなわち、
undefinedの値に向かって関数実行しとるやろ!関数やないから実行できへんで」という意味です。

ざっと見た感じ、それが起こりそうな箇所は見つけられませんでした。
行番号とかを見ながら関数実行している箇所を探しましょう。

var [data] = await pool.query(Sql);

ん?JSの変数は大文字・小文字を区別します。
まぁ、コードの貼り付け時のミスということでスルーしますが、

実践でやると大ハマリする原因です。
エディタにLinter等のツールを入れて自動的にチェックしてくれるような仕組みを導入しましょう。

エディタがVSCode等のモダンなエディタなら
導入方法を記載した記事がゴロゴロヒットするので簡単に入れられると思います。

js

1app.use('/*',async( request, response ) => {

この追加した関数はミドルウェアですが、
第三引数のnextを定義して引き継いでいないということは、
最終的にこの第三引数のnextは実行していないってことですよね?

app.useだろうが、app.getだろうが
一度該当する箇所に入ればルーティングは「終わった」と判断する作りになり、
以降の実行が打ち切られる事になります。

参考サイト: ミドルウェアの使用 - Express

それを防ぐ為の第三引数nextだと言うことをちゃんと理解しておいてください。
nextが実行されて初めて「そうなんだ、これはあくまでミドルウェアでメインの処理がまだあるねんな?」と判断してくれます。

console.log("Successful");

なーにがsuccessfulやねん。
SQLの実行に失敗してもエラーを握りつぶしているのでここ通過しちゃいますよ。

内部でasync関数版try-catchの仕組みを利用してSQL文を発行していますが、
失敗した時はresponseで直接ステータスコードの400番なりを返して終了させるような作りにしましょう。

投稿2021/01/28 09:42

miyabi-sun

総合スコア21203

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

HYDESA

2021/01/29 04:38 編集

miyabi-sunさん、いつもお世話になっています。 ご丁寧なご回答、ありがとうございました。 `app.use` のところはnext()を入れていったら、上記で正常に稼働しましたので、そのままにしておりました。このご教示で、nextを追記しておきました。 `console.log("Successful");` はブラウザ側の話だったんですね。 お陰様で理解できまして、 `response.status(404).send('404 error, Page Not found');` を`catch`の中に組み込みました。 確かにエラー時はタブ内でグルグル回っていたので、それが無くなりスッキリしています。 あとはfunction内でのquery()が出来ないので、もう少し調べています。 ただ、`catch`で`response.state(404)`を付けたおかげなのか、呼び出しは1回で済むようになりました。 nodeの経験値ゼロですので、呼び出し1回になったのが、かなり???状態ですが、何とか理解したいと思います。
HYDESA

2021/01/29 06:16

その後、調査をしておりましたが async function getSQLData(sql){ var [data] = await pool.execute(sql);// const pool = mysql.createPool{}で宣言してます return data; } 上記に変えてdataの中身を見るべく、console.log('%j',pool.execute(sql)); としたところ {} だけしか表示されませんでした(query()も同様でした)。 どうやら関数化すると、execute()もしくはquery()は実行されていない?ようです。 もう少し探ってみます。
HYDESA

2021/01/29 06:49

関数化のところ、自己解決しました。 function にこだわっていましたが、 下記のサイトがとても参考になりました。 https://teratail.com/questions/118317 async function getSQLData(sql){... これを const getSQLData = async()=>{... 実行はtry{}の中で await getSQLData(); としたら、無事にできるようになりました。 なお、ベストアンサーはいろいろ教えてくださったmiyabi-sunさんにしました。 miyabi-sunさん、ありがとうございましたm(_ _)m
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問