LoginSignup
9
6

More than 1 year has passed since last update.

Next.jsのapiでMySQL実行。クエリログの書き出しも

Last updated at Posted at 2021-10-24

まず、インストールするライブラリです。


  1. npm install serverless-mysql(必須)
    →MySQL実行用

  2. npm i sql-template-strings(ほぼ必須)
    →MySQLのセキュリティ用(インジェクション防止)

  3. 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に入れた方が良いかと思います。

以上です。

9
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
6