Skip to content

Mongoose

Connect a gateway or http bot to MongoDB with the mongoose plugin, which opens and closes the connection with your bot.

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 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 (opens in a new tab), so schemas and queries you've written with Mongoose before work the same here.

pnpm add @seedcord/plugin-mongoose mongoose
src/bot.ts
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: './handlers' },
        commands: { path: './commands' },
        events: { path: null }
    },
    subscribers: { path: null }
}).attach('db', Mongoose, {
    dir: './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 that runs on a server. Mongoose is a server-only plugin, so on an http bot for Cloudflare Workers the same call is a compile error, and a cast that gets past it throws CorePluginScopeMismatch. A Workers bot attaches other plugins as usual.

A real bot reads the connection string from its environment. process.env.MONGO_URI has the type string | undefined, which uri rejects. Envapter.getRequired('MONGO_URI', Converters.String) from envapt returns a string, and it throws when the variable is unset. Env files and modes covers reading config that way.

The options

optionrequiredwhat it sets
diryesthe folder the plugin scans for service classes. A relative path is relative to root, and the folder has to sit inside it, as the folder rules describe
uriyesthe MongoDB connection string
nameyesthe database, passed to mongoose as dbName
connectionOptionsnoany mongoose.ConnectOptions, merged over what the plugin sets
timeoutnohow long disconnecting may take during shutdown, in milliseconds. Defaults to 10000

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 PluginConnectionFailed. Its message includes 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 PluginDisconnectFailed.

The mongoose instance

Most of your queries go through service classes. 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.

const state = this.core.db.connection.connection.readyState;
const state: ConnectionStates
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.

Warning

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 PluginServicesNotReady.

Next, write the service classes the plugin loads from dir.