# Mongoose

Connect a gateway or http bot to MongoDB with the mongoose plugin. Covers installing and attaching it, its options, how it starts and stops, and reading the mongoose instance.

A bot that stores data in MongoDB needs the connection open before the first handler reads it, and closed when the bot shuts down. [`@seedcord/plugin-mongoose`](https://docs.seedcord.org/packages/plugin-mongoose/latest) opens that connection during startup and loads your service classes from a directory. The plugin opens and closes the connection. Everything else is plain [Mongoose](https://mongoosejs.com/docs/), so schemas and queries you've written with Mongoose before work the same here.

```sh
pnpm add @seedcord/plugin-mongoose mongoose
```

```ts title="src/bot.ts"
import { resolve } from 'node:path';

import { Seedcord } from '@seedcord/gateway';
import { Mongoose } from '@seedcord/plugin-mongoose';
import { GatewayIntentBits } from 'discord.js';

export const seedcord = new Seedcord({
    bot: {
        clientOptions: { intents: [GatewayIntentBits.Guilds] },
        interactions: {
            path: resolve(import.meta.dirname, './handlers')
        },
        commands: {
            path: resolve(import.meta.dirname, './commands')
        },
        events: { path: null }
    },
    subscribers: { path: null }
}).attach('db', Mongoose, {
    dir: resolve(import.meta.dirname, './services'),
    uri: 'mongodb://localhost:27017/',
    name: 'seedcord',
    connectionOptions: { serverSelectionTimeoutMS: 5000 },
    timeout: 15_000
});

export default seedcord;
```

This sample builds a gateway bot. The same `attach` call works on an http `Seedcord` from `@seedcord/http`. An edge bot can't [attach plugins](/plugins) at all.

## The options

{/* prettier-ignore-start */}

| option              | required | what it sets                                                                        |
| ------------------- | -------- | ----------------------------------------------------------------------------------- |
| `dir`               | yes      | the directory the plugin scans for service classes                                  |
| `uri`               | yes      | the MongoDB connection string                                                       |
| `name`              | yes      | the database, passed to mongoose as `dbName`                                        |
| `connectionOptions` | no       | any `mongoose.ConnectOptions`, merged over what the plugin sets                     |
| `timeout`           | no       | how long disconnecting may take during shutdown, in milliseconds. Defaults to 10000 |

{/* prettier-ignore-end */}

`connectionOptions` merges last over everything the plugin passes to `mongoose.connect()`, so you can override any setting the plugin chose. A `dbName` key in that object replaces the database your `name` picked.

> **Warning**
>
> When the environment is `production`, the plugin adds `tls: true` and `ssl: true`. seedcord reads the environment from the first of `ENVIRONMENT`, `ENV`, `NODE_ENV`, or `MODE` that you set. If you run production mode against a local database without TLS, that database refuses the connection, so set `tls: false` and `ssl: false` in `connectionOptions`.

## Startup and shutdown

A bot that fails halfway through startup can leave its MongoDB connection open. The mongoose plugin closes that connection for you. `init()` connects, then loads every service class from `dir`. If either step fails, `init()` closes the connection before it rethrows the error.

If the connection fails, `init()` throws `PluginMongooseConnectionFailed`. Its message names the database from your `name` option, and mongoose's own error is in its `cause`.

Shutdown runs the same steps backwards. `dispose()` clears the service registry and every loaded model, then closes the connection. If closing fails, `dispose()` throws `PluginMongooseDisconnectFailed`.

## The mongoose instance

Most of your queries go through [service classes](/plugins/mongoose-services). Some work doesn't belong in a service, like checking whether the bot is still connected to MongoDB. For that, use `this.core.db.connection`, the Mongoose instance that `mongoose.connect()` returned. Its `connection` property is Mongoose's `Connection` object, and its `model()` method builds a model.

```ts
const state = this.core.db.connection.connection.readyState;

await this.reply(`Connection state ${String(state)}.`);
```

The handler reads `readyState` from Mongoose's `Connection`, which is `1` while the bot is connected. `this.core.db` only has a type once you've run [`seedcord codegen`](/plugins/typing).

`connection` and `services` are only ready after `init()` resolves. If you read `connection` before then, you get `undefined`, even though its type says it's always set. Reading `services` before then throws `PluginMongooseServicesNotReady`.

Next, write the [service classes](/plugins/mongoose-services) the plugin loads from `dir`.
