LoginSignup
1
0

More than 3 years have passed since last update.

Nodejs用のモデルライブラリを作ってみた【Express】

Last updated at Posted at 2020-06-19

リポジトリ

https://github.com/kbc18a11/oreoreExpress
ライブラリ本体はoreoreExpress/database/AbstractModel.jsです。

解説用の構成や注意点

  • FW:Express
  • ディレクトリ構成:WebStormの新規Expressプロジェクトの状態にプロジェクト名/databaseプロジェクト名/modelというディレクトリを生成
  • 本記事はチーム開発のメンバーのマニュアルとしても書いていますので、ライブラリには直接関係ないことも書いています。

今回利用しているtestsテーブルは以下の構成になっています

id text created_at updated_at

使い方

リポジトリのものを利用するのであれば、以下のコマンドを実行してください。

npm install mysql2

ライブラリだけを使用するのであれば、mysql2date-utlisをインストールします

npm install mysql2
npm install date-utlis

そして以下のとおり、mysql2の設定ファイルを作ります。

プロジェクト名/database/mysqlConnection
const mysql = require('mysql2');
//MySQLの接続設定
const connection = mysql.createConnection({
    host: 'localhost',
    user: '',
    password: '',
    database: ''
}).promise();

module.exports = connection;

下記の通り、作りたいモデルクラスに設定します。
また以下のメソッドをオーバーライド、オーバーロードします。
abstractTABLE_NAME
abstractVALIDATIONRULES
update

メソッドabstractTABLE_NAMEは、連携しているテーブル名のです。
メソッドabstractVALIDATIONRULESは、バリエーションルールを記述する所です。
バリエーションのルールはvalidatorjsのものになります。
https://www.npmjs.com/package/validatorjs

プロジェクト名/model/Tests
const AbstractModel = require('./AbstractModel');
const connection = require('../database/mysqlConnection');
require('date-utils');

class Tests extends AbstractModel {
    constructor() {
        super();
    }

    /**
     * テーブル名
     * @override
     * @returns {string}
     */
    static get abstractTABLE_NAME() {
        return 'tests';
    }

    /**
     * バリデーションルール
     * @override
     * @return {Object}
     */
    static get abstractVALIDATIONRULES() {
        return {
            get:{
                rule: {
                    id: 'required|integer'
                },
                errorMessage: {
                    required: '必須項目です。',
                    integer: '数値で入力してください'
                }
            },
            //POSTリクエスト用
            post: {
                rule: {
                    text: 'required'
                },
                errorMessage: {
                    required: '必須項目です。',
                }
            },
            //PUTリクエスト用
            put: {
                rule: {
                    id: 'required|integer',
                    text: 'required'
                },
                errorMessage: {
                    required: '必須項目です。',
                    integer: '数値で入力してください'
                }
            },
            //DELETEリクエスト用
            delete: {
                rule: {
                    id: 'required|integer'
                },
                errorMessage: {
                    required: '必須項目です。',
                    integer: '数値で入力してください'
                }
            }
        };
    }

    /**
     * UPDATE文の準備を行って、親クラスのupdate()に実行をさせる
     * @param {Object} insertParam
     */
    static async update(insertParam) {
        //UPDATE文
        const sql = `UPDATE ${this.abstractTABLE_NAME} SET text = ?,updated_at = ? WHERE id = ?`;

        //create_at用の日付時間取得
        insertParam.updated_at = new Date().toFormat('YYYY-MM-DD HH:MI:SS');

        //SQLの実行
        await super.update(insertParam, sql);
    }
}

module.exports = Tests;

機能

基本的にライブラリの機能を使う場合は、Expressのルーティングファイルの無名関数にasyncを付与します。

プロジェクト名/routes/ルーティングファイル
router.get('/tests', async (req, res, next) => {

そして、ルーティングファイルで以下のライブラリを取り組みます。

プロジェクト名/routes/ルーティングファイル
const express = require('express');
const router = express.Router();
const validator = require('validatorjs');
const Tests = require('../model/Tests');

ライブラリでデータベースを操作するメソッドは、Promiseオブジェクトがかかわっているため、呼び出しの際には、awaitを付与します。

プロジェクト名/routes/ルーティングファイル
//レコードをすべて取得
await Test.all()
//引数idのカラム取得
await Test.find(id)
//引数idの存在確認を行う
await Test.existId(id)
//引数paramの値で新規登録を行う
await Test.insert(param)
//引数paramの値で更新を行う
await Test.update(param)
//引数idのレコードを削除する
await Test.delete(id)

all() レコードをすべて取得

プロジェクト名/routes/test.js
/**
 * @GET
 * testsのレコードをすべて取得
 */
router.get('/tests', async (req, res, next) => {

    try {
        //レコードをすべて取得
        const allRows = await Tests.all();

        //レコードを返す
        return res.send(allRows);
    } catch (error) {
        //レコードの取得失敗時
        console.log(error);
        res.status(500);
        return res.send({'error': 'サーバー側でエラーが発生しました'});
    }

});

http://localhost:3000/testsGETでアクセスします。

http
[
    {
        "id": 1,
        "text": "これはテストです",
        "created_at": "2020-06-18T16:50:45.000Z",
        "updated_at": null
    },
    {
        "id": 2,
        "text": "これはテストです",
        "created_at": "2020-06-18T16:50:50.000Z",
        "updated_at": null
    },
    {
        "id": 3,
        "text": "これはテストです",
        "created_at": "2020-06-18T16:50:51.000Z",
        "updated_at": null
    }
]

find(id) 引数idのカラム取得

プロジェクト名/routes/test.js
/**
 * @GET
 * 指定されたidのカラムを取得
 */
router.get('/tests/:id', async (req, res, next) => {
    //バリデーションの検証を受ける値
    const verificationValue = {
        id: req.params.id
    }
    //バリデーションの結果にエラーがあるかのチェック
    const validation = new validator(
        verificationValue,
        Tests.abstractVALIDATIONRULES.get.rule,
        Tests.abstractVALIDATIONRULES.get.errorMessage
    );
    if (validation.fails()) {
        //エラーを422で返す
        return res.status(422).send({errors: validation.errors.all()});
    }

    try {
        //レコードを取得
        const row = await Tests.find(verificationValue.id);
        //レコードを返す
        return res.send(row);
    } catch (error) {
        //レコードの取得失敗時
        console.log(error);
        res.status(500);
        return res.send({'error': 'サーバー側でエラーが発生しました'});
    }
})

http://localhost:3000/(testのidを指定)GETでアクセスします。

http
[
    {
        "id": 1,
        "text": "これはテストです",
        "created_at": "2020-06-18T16:50:45.000Z",
        "updated_at": null
    }
]

また、URIのidを指定する所が以下のとおり数値以外の場合は、エラーメッセージを返します。
http://localhost:3000/aaaa77

http
{
    "errors": {
        "id": [
            "数値で入力してください"
        ]
    }
}

insert() 引数paramの値で新規登録を行う

プロジェクト名/routes/test.js
/**
 * @POST
 * testsに新しいレコードを挿入
 */
router.post('/test', async (req, res, next) => {
    //バリデーションの検証を受ける値
    const verificationValue = {
        text: req.query.text
    }
    //バリデーションの結果にエラーがあるかのチェック
    const validation = new validator(
        verificationValue,
        Tests.abstractVALIDATIONRULES.post.rule,
        Tests.abstractVALIDATIONRULES.post.errorMessage
    );
    if (validation.fails()) {
        //エラーを422で返す
        return res.status(422).send({errors: validation.errors.all()});
    }

    try {
        //レコードの挿入開始
        await Tests.insert({text: req.query.text});
        return res.send({'insertResult': true});
    } catch (error) {
        //レコードの挿入失敗時
        console.log(error);
        return res.status(500).send({'insertResult': false});
    }
});

http://localhost:3000/test?text=テストやりたいPOSTでアクセスします。

http
[
    {
        "id": 1,
        "text": "これはテストです",
        "created_at": "2020-06-18T16:50:45.000Z",
        "updated_at": null
    }
]

リクエストのボディにtextがない場合は、エラーメッセージを返します。

http
{
    "errors": {
        "text": [
            "必須項目です。"
        ]
    }
}

update(param) 引数paramの値で更新を行う existId(id) 引数idの存在確認を行う

プロジェクト名/routes/test.js
/**
 * @PUT
 * レコードの更新
 */
router.put('/test/:id', async (req, res, next) => {
    //バリデーションの検証を受ける値
    const verificationValue = {
        id: req.params.id,
        text: req.query.text
    }
    //バリデーションの結果にエラーがあるかのチェック
    const validation = new validator(
        verificationValue,
        Tests.abstractVALIDATIONRULES.put.rule,
        Tests.abstractVALIDATIONRULES.put.errorMessage
    );
    if (validation.fails()) {
        //エラーを422で返す
        return res.status(422).send({errors: validation.errors.all()});
    }

    //idは存在しないか?
    if (!await Tests.existId(verificationValue.id)) {
        //エラーを422で返す
        return res.status(422).send({
            errors: {
                id: ['idが存在しません']
            }
        });
    }

    try {
        //レコードの更新開始
        await Tests.update(verificationValue);
        return res.send({'updateResult': true});
    } catch (error) {
        //レコードの更新失敗時
        console.log(error);
        return res.status(500).send({'updateResult': false});
    }

});

http://localhost:3000/test?text=テストやりたいPUTでアクセスします。

http
{
    "updateResult": true
}

URIのidを指定する所が以下のとおり数値以外やリクエストのボディにtextがない場合は、エラーメッセージを返します。
http://localhost:3000/test/aaa

http
{
    "errors": {
        "id": [
            "数値で入力してください"
        ],
        "text": [
            "必須項目です。"
        ]
    }
}

そして、find(id)の引数のidがレコードに存在しない場合、このようなエラーを返します

http
{
    "errors": {
        "id": [
            "idが存在しません"
        ]
    }
}

delete(id) 引数idのレコードを削除する

プロジェクト名/routes/test.js
/**
 * @DELETE
 * レコードの削除
 */
router.delete('/test/:id', async (req, res, next) => {
    //バリデーションの検証を受ける値
    const verificationValue = {
        id: req.params.id,
    }
    //バリデーションの結果にエラーがあるかのチェック
    const validation = new validator(
        verificationValue,
        Tests.abstractVALIDATIONRULES.delete.rule,
        Tests.abstractVALIDATIONRULES.delete.errorMessage
    );
    if (validation.fails()) {
        //エラーを422で返す
        return res.status(422).send({errors: validation.errors.all()});
    }

    //idは存在しないか?
    if (!await Tests.existId(verificationValue.id)) {
        //エラーを422で返す
        return res.status(422).send({
            errors: {
                id: ['idが存在しません']
            }
        });
    }

    try {
        //レコードの削除開始
        await Tests.delete(verificationValue.id);
        return res.send({'deleteResult': true});
    } catch (error) {
        //レコードの削除失敗時
        console.log(error);
        return res.status(500).send({'deleteResult': false});
    }


})

http://localhost:3000/(testのidを指定)DELETEでアクセスします。

http
[
    {
        "id": 1,
        "text": "これはテストです",
        "created_at": "2020-06-18T16:50:45.000Z",
        "updated_at": null
    }
]

URIのidを指定する所が以下のとおり数値以外の場合は、エラーメッセージを返します。
http://localhost:3000/aaaa77

http
{
    "errors": {
        "id": [
            "数値で入力してください"
        ]
    }
}

そして、find(id)の引数のidがレコードに存在しない場合、このようなエラーを返します

http
{
    "errors": {
        "id": [
            "idが存在しません"
        ]
    }
}
1
0
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
1
0