Skip to content

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 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 { 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 at all.

The options

optionrequiredwhat it sets
diryesthe directory the plugin scans for service classes
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 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. 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.

hover for typestap for types, arrow keys walk the tokens
const const state: ConnectionStatesstate = this.BaseHandler<ChatInputCommandInteraction<"cached">, Core>.core: Corecore.Core.db: Mongoosedb.Mongoose.connection: typeof import("mongoose")connection.const connection: Connectionconnection.Connection.readyState: ConnectionStatesreadyState;

await this.RepliableHandler<ChatInputCommandInteraction<"cached">, Core, SentMessage, BufferResolvable | Stream | JSONEncodable<...> | Attachment | AttachmentBuilder | AttachmentPayload, ReplySender>.reply(response: string | ReplyResponse<BufferResolvable | Stream | JSONEncodable<APIAttachment> | Attachment | AttachmentBuilder | AttachmentPayload>, opts?: SendOpts): Promise<SentMessage>reply(`Connection state ${
var String: StringConstructor;
(value?: any) => string
String
(const state: ConnectionStatesstate)}.`);

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.

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 the plugin loads from dir.