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

## はじめに

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

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

mongoose の Top ページ ([ココ](https://mongoosejs.com/))に書いてある

```javascript
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

```yaml: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](https://hub.docker.com/_/mongo/))のまま。永続化しただけ。express はなくても良いけど、MongoDB にも不慣れで便利なのでありがたく。

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

## うまくいかなかったやり方
```
├── app.js
└── config
    └── db.js
```

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

```javascript: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();
  });
```

実行するとエラー

```bash
$ 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 の渡し方がいかんのか？と思って以下のように変更。

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

```javascript: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.0``` を ```localhost``` に変えてみたり、docker-compose に書いた ```monogo``` に変えてみたりしたけど、当然ダメ。

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

```javascript: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 をオプションで渡す

https://mongoosejs.com/docs/connections.html#options

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

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

```javascript: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"));
```

実行

```bash
$ node app.js
successfully connected to the database
meow
```

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

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

## つながらなかった理由
わかりまｓｎ笑

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

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