LoginSignup
3
4

More than 5 years have passed since last update.

Nuxt.js+expressでsequelizeを使いたい

Posted at

何したいの?

https://github.com/nuxt-community/express-template
こちらのNuxt.js公式テンプレートを使って開発中の出来事になります。
MySQLに接続するためにサーバーサイドにSequelizeを導入したのですが、
いざnpm run devすると

Error: Cannot find module '/home/User/Project-Name/build/server/models/user.js'

というエラーが。
buildフォルダにはjsファイルとソースマップだけ。
あれぇ?

原因を探ろう

Nuxt.jsは元からwebpackによるJSファイルのバンドル機能が組み込まれています。
requireやimport文が一行でも書かれていれば見逃すことなく1ファイルにまとめてくれるすごい奴のはずなんですが。

sequelizeのソースを眺める

server/models/index.js

'use strict';
require('dotenv').config();

var fs        = require('fs');
var path      = require('path');
var Sequelize = require('sequelize');
var basename  = path.basename(__filename);
var env       = process.env.NODE_ENV || 'development';
var config    = require(__dirname + '/../config/config.json')[env];
var db        = {};

if (config.use_env_variable) {
    console.log(process.env[config.use_env_variable]);
  var sequelize = new Sequelize(process.env[config.use_env_variable], config);
} else {
  var sequelize = new Sequelize(config.database, config.username, config.password, config);
}

fs
  .readdirSync(__dirname)
  .filter(file => {
    return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js');
  })
  .forEach(file => {
    var model = sequelize['import'](path.join(__dirname, file)); // <-- モデル読み込みはここ!
    db[model.name] = model;
  });

Object.keys(db).forEach(modelName => {
  if (db[modelName].associate) {
    db[modelName].associate(db);
  }
});

db.sequelize = sequelize;
db.Sequelize = Sequelize;

module.exports = db;
node_modules/sequelize/lib/sequelize.js
//382行目から

  import(path) {
    // is it a relative path?
    if (Path.normalize(path) !== Path.resolve(path)) {
      // make path relative to the caller
      const callerFilename = Utils.stack()[1].getFileName();
      const callerPath = Path.dirname(callerFilename);

      path = Path.resolve(callerPath, path);
    }

    if (!this.importCache[path]) {
      let defineCall = arguments.length > 1 ? arguments[1] : require(path);
      if (typeof defineCall === 'object') {
        // ES6 module compatibility
        defineCall = defineCall.default;
      }
      this.importCache[path] = defineCall(this, DataTypes);
    }

    return this.importCache[path];
  }

モデルを読み込む部分がパス文字列を引数として渡す形になっているようです。
ビルド前の__dirnameとビルド後の__dirnameが変わってしまうのでエラーが起きている感じですね。

解決策

色々調べたのですが、webpack側でどうにかする方法はおそらく無いようです。
というわけで強引ですが、copy-webpack-pluginを使って、modelフォルダをbuildフォルダにコピーすることにします。

backpack.config.js
var CopyWebpackPlugin = require('copy-webpack-plugin');

module.exports = {
  webpack: (config, options, webpack) => {
    config.entry.main = './server/index.js'
    config.plugins = [
        new CopyWebpackPlugin([{from: './server/models/', to:'./server/models/'}], {ignore: ['index.js']})
    ]
    return config
  }
}

テンプレートで使われているのがbackpack.jsなので、webpackの場合は適宜読み替えてください。
fromとtoの文字列が同じですが、toはwebpack出力先のフォルダがルートになるためです。
実際は、fromが”ルートディレクトリ/server/models/”なのに対して、
toは”ルートディレクトリ/build/server/models/”となります。

おわりに

もっとエレガントな解決法があったら何卒ご教授くださいませ…

3
4
1

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
3
4