かすみん日記

暇なときに何か喋ります

node.jsでMySQLを使うときは、hostにはlocalhostじゃなくて127.0.0.1を指定する【MySQL 8】

環境

% sw_vers
ProductName:    macOS
ProductVersion: 12.4
BuildVersion:   21F79

% node -v
v18.4.0

% mysql --version
mysql  Ver 8.0.30 for macos12.4 on x86_64 (Homebrew)

% brew -v
Homebrew 3.5.9
Homebrew/homebrew-core (git revision 27007d7668a; last commit 2022-08-20)
Homebrew/homebrew-cask (git revision d2da3c1a45; last commit 2022-08-20)

概要

MySQLの設定ファイルmy.cnfに以下の記載

bind-address = 127.0.0.1

がある場合に、node(のmysql2パッケージ)でhostlocalhostを指定して接続しようとすると失敗します。

なので、localhostではなく、直接127.0.0.1を指定します。

const mysql = require('mysql2');

const connection = mysql.createConnection({
  host: '127.0.0.1', // localhostだとダメ
  user: 'root',
  database: 'hoge'
});

調べたこと

特にHomebrewでMySQLをインストールした人がこの問題に直面しているかと思います。

HomebrewでMySQLをインストールすると、自動で設定ファイルmy.cnfが作成されます:

% cat /usr/local/etc/my.cnf
# Default Homebrew MySQL server config
[mysqld]
# Only allow connections from localhost
bind-address = 127.0.0.1
mysqlx-bind-address = 127.0.0.1

コメント文からもわかるように、bind-addressIPアドレスを指定すると、そのアドレス以外からのアクセスを禁止します。

基本的に、127.0.0.1にはlocalhostというホスト名が設定されています:

% cat /etc/hosts    
##
# Host Database
#
# localhost is used to configure the loopback interface
# when the system is booting.  Do not change this entry.
##
127.0.0.1       localhost
255.255.255.255 broadcasthost
::1             localhost

なので、上記のようにbind-addressの設定がされていても、localhostからでもアクセスできるはずです。

実際、ターミナルからであれば、localhostでログインできます:

% mysql.server start
Starting MySQL
.. SUCCESS! 

% mysql -u root -h localhost
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 14
Server version: 8.0.30 Homebrew

略

mysql> 

ところが、nodeの方では、ホスト名にlocalhostを指定してMySQLに接続しようとすると失敗します。

下記内容のファイルをtest.jsという名前で保存:

const mysql = require('mysql2');

const connection = mysql.createConnection({
  host: 'localhost',
  user: 'root',
  password: ''
});

connection.query(
  'SELECT 1 + 1',
  function(err, results) {
    console.log(results);
  }
);

connection.end();

実行してみると、エラーが出る:

% node test.js
undefined
node:events:515
      throw er; // Unhandled 'error' event
      ^

Error: connect ECONNREFUSED ::1:3306
    at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1237:16)
Emitted 'error' event on Connection instance at:
    at Connection._notifyError (/Users/hoge/test/node_modules/mysql2/lib/connection.js:236:12)
    at Connection._handleFatalError (/Users/hoge/test/node_modules/mysql2/lib/connection.js:167:10)
    at Connection._handleNetworkError (/Users/hoge/test/node_modules/mysql2/lib/connection.js:180:10)
    at Socket.emit (node:events:537:28)
    at emitErrorNT (node:internal/streams/destroy:151:8)
    at emitErrorCloseNT (node:internal/streams/destroy:116:3)
    at process.processTicksAndRejections (node:internal/process/task_queues:82:21) {
  errno: -61,
  code: 'ECONNREFUSED',
  syscall: 'connect',
  address: '::1',
  port: 3306,
  fatal: true
}

Node.js v18.4.0

上記コードのlocalhost部分を127.0.0.1に置き換えます:

  // host: 'localhost',
  host: '127.0.0.1',

再度実行してみると、ちゃんとクエリが実行されました:

% node test.js
[ { '1 + 1': 2 } ]

これは奇妙ですね。mysql2パッケージがブラックボックスなのでこれ以上何とも言えません。

node + MySQLの解説記事を見ていると、ほとんどの記事でhost: 'localhost'と書いています。

おそらく、それでクエリ実行できているのは、my.cnfbind-addressの記載がないからでしょう。

あるいはバージョンの違いでしょうか。そこまでは調べられていません。

とにかく、HomebrewでMySQLをインストールするとmy.cnfbind-addressの設定が自動で作成されてしまいますので、その設定をコメントアウトするか、hostに直接127.0.0.1を指定するかのいずれかの対応が必要になります。

以上。

補足

"mysql2"パッケージ

nodeでMySQL接続するためのパッケージですが、mysqlではなくmysql2を使っているのは、前者がMySQL 8系に非対応だからです。

私はそもそも初心者で何も知らないのですが、mysql2mysqlと同じように使えるらしいです(メソッド名とかが同じ?)。

my.cnfの場所

my.cnfが配置されうる場所は以下のようにして調べられるらしいです:

% mysql --help | grep "/my.cnf"
/etc/my.cnf /etc/mysql/my.cnf /usr/local/etc/my.cnf ~/.my.cnf 

HomebrewでMySQLをインストールしたときに作成されるのは/usr/local/etc/my.cnfのみです。

他の3つもcatしてみましたが、いずれもファイルが存在していませんでした。

なお、既に/usr/local/etc/my.cnfが存在している状態で、brew install mysqlしたときは、/usr/local/etc/my.cnfに変更は加わりません。

なので昔にMySQLの環境を作っていた人は、Homebrewからインストールし直しても上記問題には気がつかなかったかも知れません。

ちなみに、brew uninstall mysqlしてもこの設定ファイルは消去されないので、いらなくなったら手動で削除して下さい:

rm /usr/local/etc/my.cnf

参考

MySQL 8系ではmysqlではなくmysql2を使わないとダメだと気付かせてくれたサイト

teratail.com

mysql2パッケージ

www.npmjs.com

同様のエラーに対し試行錯誤している記事。これ読んで下さい

full-push.com

インストールしたMySQLを完全に消したい記事。いつか必要になるかも

qiita.com