まず、インストールするライブラリです。
- npm install serverless-mysql(必須)
→MySQL実行用 - npm i sql-template-strings(ほぼ必須)
→MySQLのセキュリティ用(インジェクション防止) - npm install date-utils(あれば良い)
→ただ日付を簡単に取得したいだけ(クエリログとMySQL内のtimestamp用)
Next.jsの/pages/api内のエンドポイントを呼び出して、その中でMySQLを実行します。
Next.jsのapi領域は、Node.js環境なので、
今回使用するserverless-mysql以外にも、mysqlのライブラリがいくつかあり、
Node.js MySQLなども、実行することができました。
今回は、serverless-mysqlの使います。
完成系
/lib/mysql.jsを作成
const mysql = require('serverless-mysql'); //エスケープ
require('date-utils'); //日時簡単取得
const db = mysql({
config: {
host: process.env.MYSQL_HOST,
database: process.env.MYSQL_DATABASE,
user: process.env.MYSQL_USER,
password: process.env.MYSQL_PASSWORD
}
})
//クエリログ書き出し
const queryLogWrite = (query, values) => {
const fs = require("fs"); //ファイル書き出しモジュール
let now = new Date();
let excuteQuery = '\n' + now.toFormat('YYYY/MM/DD DDD HH:MI:SS') + query;
values.forEach(data => {
excuteQuery = excuteQuery.replace('?', data)
})
//書き込み
fs.appendFile("log/query.log", excuteQuery, (err) => {
if (err) throw err;
//正常処理
});
}
exports.query = async SQL => {
try {
//ログ書き出し
queryLogWrite(SQL.query, SQL.values);
const results = await db.query(SQL)
await db.end()
return results
} catch (error) {
return { error }
}
}
ポイント
1.
//ログ書き出し
queryLogWrite(SQL.query, SQL.values);
↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
values.forEach(data => {
excuteQuery = excuteQuery.replace('?', data)
})
の部分は、
SQL.query:バインドパラメータが「?」状態のSQL文
SQL.values:バインドパラメータの配列
となっているので、
SQL.valuesをループで回してSQL文の「?」をリプレースして完全なクエリログを作成しています。
2.「process.env.」を使用しているので、Next.jsのドキュメントを見ながら、.env.local等のファイルを作成してください。(.gitignoreのファイル設定に気をつけて)
3.require("fs")は、Node.jsが持っている機能で、これを使ってquery.logファイルを書き出す&追記します。
fs.appendFile
とすると、指定したファイルの最下行に、追加したいテキストを追記することができます。
4.
let now = new Date();
let excuteQuery = '\n' + now.toFormat('YYYY/MM/DD DDD HH:MI:SS') + query;
は、インストールした、date-utilsの機能で、js本来のgetfullyear等の年月日日時の取得をフォーマット指定で省略することができます。
今回は、クエリログ書き出し時のログ出力時間として使っています。
このファイルを、/pages/api内の別ファイルから呼び出して使用します。
今回は、/pages/api/login.tsというログイン用のエンドポイントから呼び出します。
(typescriptの書き方になっています。)
import type { NextApiRequest, NextApiResponse } from 'next'
const db = require('../../lib/mysql')
const escape = require('sql-template-strings')
type PostData = {
email: string;
password: string;
}
type QueryResult = {
loginResult: boolean
}
export default async (req: NextApiRequest, res: NextApiResponse<QueryResult>) => {
const postData: PostData = req.body;
const queryResult = await db.query(escape`
SELECT
COUNT (USER_ID) AS count
FROM
USERLIST
WHERE
EMAIL = ${postData.email}
AND PASSWORD = ${postData.password}
`)
res.status(200).json({ loginResult: 0 < queryResult[0].count ? true : false });
}
ポイント
1.ここは、../../lib/mysqlで実行するSQLを作成する役目です。
見ての通り、emailとpasswordでログイン認証を行なっています。
${postData.email}という、jsのヒアドキュメント内でjsの文字列を出力する書き方をしていますが、ヒアドキュメント開始直前に「escape」とあるのが、インストールしたsql-template-stringsで、
これにより、SQLインジェクションを防げるそうです。
const queryResult = await db.query(escape[SQL文]
)
が1セットとして見れば良いかと思います。
以上の内容で、
MySQLのUSERLISTテーブル内から、email,passwordで認証を行なって、
当てはまるレコードが1件でもあればtrueを返し、
同時に上記のクエリログを/logs/query.logに書きだす処理を作成できました。
パスワードのハッシュ化、try,catch等、追加で書くとさらに良くなると思います。
※今回のクエリログは、localhost開発時には便利ですが、
実際に本番デプロイした後に(例えばvercelデプロイ)ログファイルを見れるか怪しいので、あくまで開発のお供として考えてください。
あと、logフォルダも.gitignoreに入れた方が良いかと思います。
以上です。