環境
% 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パッケージ)でhostにlocalhostを指定して接続しようとすると失敗します。
なので、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-addressにIPアドレスを指定すると、そのアドレス以外からのアクセスを禁止します。
基本的に、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.cnfにbind-addressの記載がないからでしょう。
あるいはバージョンの違いでしょうか。そこまでは調べられていません。
とにかく、HomebrewでMySQLをインストールするとmy.cnfにbind-addressの設定が自動で作成されてしまいますので、その設定をコメントアウトするか、hostに直接127.0.0.1を指定するかのいずれかの対応が必要になります。
以上。
補足
"mysql2"パッケージ
nodeでMySQL接続するためのパッケージですが、mysqlではなくmysql2を使っているのは、前者がMySQL 8系に非対応だからです。
私はそもそも初心者で何も知らないのですが、mysql2はmysqlと同じように使えるらしいです(メソッド名とかが同じ?)。
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を使わないとダメだと気付かせてくれたサイト
mysql2パッケージ
同様のエラーに対し試行錯誤している記事。これ読んで下さい
インストールしたMySQLを完全に消したい記事。いつか必要になるかも