mongoose で docker でたてた MongoDB に接続するのにはまった話


はじめに

Python でせっせと実装していたものが、npm install すれば、あっという間に出来てしまうことを知り、少し落ち込んだりもしたけど、Node.js で書き直している私です。Node.js は超初心者です。

開発は1人でやっていて、まだ試しに作ってるだけの状態で、Mac にあれこれ DB をたてるのが嫌なので、DB だけ docker でやっちゃおう!とやってみたところ、ちっとも繋がりませんでした。

mongoose の Top ページ (ココ)に書いてある

const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost:27017/test', {useNewUrlParser: true, useUnifiedTopology: true});

const Cat = mongoose.model('Cat', { name: String });

const kitty = new Cat({ name: 'Zildjian' });
kitty.save().then(() => console.log('meow'));

これのコピペが実行できないという絶望的な状況からのスタート。

動作環境

OS: MacOS Catalina Version 10.15.5 (19F101)
node: v12.18.1
mongoose: 5.9.20
MongoDB: 4.2.8
Docker: version 19.03.8, build afacb8b
docker-compose: version 1.25.5, build 8a1c60f6

docker-compose

docker-compose.yml
version: "3.1"

services:
  mongo:
    image: mongo
    restart: always
    environment:
      MONGO_INITDB_ROOT_USERNAME: user
      MONGO_INITDB_ROOT_PASSWORD: secret1234
    ports:
      - 27017:27017
    volumes:
      - ./configdb:/data/configdb
      - mongo_local_marketing:/data/db

  mongo-express:
    image: mongo-express
    restart: always
    ports:
      - 8081:8081
    environment:
      ME_CONFIG_MONGODB_ADMINUSERNAME: user
      ME_CONFIG_MONGODB_ADMINPASSWORD: secret1234

volumes:
  mongo_local_marketing:
    driver: local

ほぼ、公式(docker hub: mongo)のまま。永続化しただけ。express はなくても良いけど、MongoDB にも不慣れで便利なのでありがたく。

で、sandbox という名前の database を作っておく。

うまくいかなかったやり方

├── app.js
└── config
    └── db.js
config/db.js
module.exports = {
  url: "mongodb://user:[email protected]:27017/sandbox",
};
app.js
const mongoose = require("mongoose");
const dbConfig = require("./config/db");

// connect mongodb
mongoose
  .connect(dbConfig.url, {
    useNewUrlParser: true,
    useUnifiedTopology: true,
  })
  .then(() => {
    console.log("successfully connected to the database");
  })
  .catch((err) => {
    console.log("error connecting to the database");
    console.log(err);
    process.exit();
  });

実行するとエラー

$ node app.js
error connecting to the database
MongooseServerSelectionError: Authentication failed.
    at NativeConnection.Connection.openUri (.../node_modules/mongoose/lib/connection.js:830:32)
    at Mongoose.connect (.../node_modules/mongoose/lib/index.js:335:15)
    at Object.<anonymous> (.../app.js:10:4)
    at Module._compile (internal/modules/cjs/loader.js:1138:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1158:10)
    at Module.load (internal/modules/cjs/loader.js:986:32)
    at Function.Module._load (internal/modules/cjs/loader.js:879:14)
    at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:71:12)
    at internal/main/run_main_module.js:17:47 {
  reason: TopologyDescription {
    type: 'Single',
    setName: null,
    maxSetVersion: null,
    maxElectionId: null,
    servers: Map { '0.0.0.0:27017' => [ServerDescription] },
    stale: false,
    compatible: true,
    compatibilityError: null,
    logicalSessionTimeoutMinutes: null,
    heartbeatFrequencyMS: 10000,
    localThresholdMS: 15,
    commonWireVersion: null
  }
}

reason... ナニコレ、さっぱり分からん。認証に失敗していることだけ分かった。これ、慣れたら reason 見て理由が分かるもの?!
user/password の typo か?と思ったけど、そうじゃない。user/password の渡し方がいかんのか?と思って以下のように変更。

config/db.js
module.exports = {
  url: "mongodb://0.0.0.0:27017/sandbox",
  user: "user",
  pwd: "secret1234",
};
app.js
const mongoose = require("mongoose");
const dbConfig = require("./config/db");

// connect mongodb
mongoose
  .connect(dbConfig.url, {
    useNewUrlParser: true,
    useUnifiedTopology: true,
    user: dbConfig.user,
    pass: dbConfig.pwd,
  })
...

ダメ。0.0.0.0localhost に変えてみたり、docker-compose に書いた monogo に変えてみたりしたけど、当然ダメ。

dbName を取ってみたら接続はできた

config/db.js
module.exports = {
  url: "mongodb://0.0.0.0:27017",
  user: "user",
  pwd: "secret1234",
};

こうすると、なんか接続はできた。しかし、MongoDB 上に Database は、

  • admin
  • config
  • local
  • sandobox

の4つが存在しており、どれにつながってるのか分からん状態。Database は増やせるし。
sandbox に最初からつながりたいんですよ、私は。たまたまつながってる状態じゃなくて、ちゃんと明示した通りにつながっていて欲しいんですよ、私は。

dbName をオプションで渡す

ここを見ると、dbName もオプションで渡せるみたいなので、そのようにしてみた。

config/db.js
module.exports = {
  url: "mongodb://0.0.0.0:27017",
  user: "user",
  pwd: "secret1234",
  dbName: "sandbox",
};
app.js
const mongoose = require("mongoose");
const dbConfig = require("./config/db");

// connect mongodb
mongoose
  .connect(dbConfig.url, {
    useNewUrlParser: true,
    useUnifiedTopology: true,
    user: dbConfig.user,
    pass: dbConfig.pwd,
    dbName: dbConfig.dbName,
  })
  .then(() => {
    console.log("successfully connected to the database");
  })
  .catch((err) => {
    console.log("error connecting to the database");
    console.log(err);
    process.exit();
  });

const Cat = mongoose.model("Cat", { name: String });

const kitty = new Cat({ name: "Zildjian" });
kitty.save().then(() => console.log("meow"));

実行

$ node app.js
successfully connected to the database
meow

にゃー。
"sandbox" database の "cats" collection にジルジャン入ってました。シンバルか!

{
    _id: ObjectId('5ef8b261164eb0103f341ef6'),
    name: 'Zildjian',
    __v: 0
}

つながらなかった理由

わかりまsn笑

同じ症状の人が見当たらなかったので、私の環境のせい?今度暇な時にゆっくり mongoose のソースコードを追ってみるかも知れない。

今日はいったん、ここまで。あと MongoDB に突っ込めばいったん開発おしまいなので、そっちをやってから。