Skip to content
sern

Dependencies

Manage objects which contain lots of state. If you were a previous user of Sapphire, dependency injection is the moral equivalent of container. Dependency injection promotes maintainability and helps organize imports.

For example, a minimal setup for any project might look like this:

  • Directorysrc/
    • index.js (your main file and client)
    • dependencies.d.ts (for intellisense)
src/index.js
1
const client = new Client({
2
...options,
3
});
4
5
await makeDependencies((root) => {
6
root.add("@sern/client", client),
7
});

Everything else is handled. However, you may want customize things.

Adding Dependencies to Root

When makeDependencies is called, sern implicility adds dependencies to the container. A few of these are shown:

  • @sern/logger: Loggers implement Logging
  • @sern/modules: A map of all command modules. ID -> Module
  • @sern/errors: Handling errors and lifetime → ErrorHandling
  • @sern/emitter: Emit events that occur throughout the lifecycle of a project → Emitter

Lifecycle Hooks

Dependencies can call a function throughout you bot’s lifetime.

Init

Your object needs to initiate things. Developers are allowed to use async and await.

  1. Do you need to perform intializing behavior for a dependency?

    src/database.js
    1
    import pg from 'pg'
    2
    const { Client } = pg
    3
    class Database {
    4
    __database = new Client()
    5
    async init() {
    6
    await this.__database.connect();
    7
    }
    8
    }
  2. Modify your Dependencies interface:

    src/dependencies.d.ts
    1
    import { Database } from './services/database.js'
    2
    interface Dependencies extends CoreDependencies {
    3
    database: Database;
    4
    }
  3. Make sure its been added:

    src/index.ts
    1
    await makeDependencies(({ add }) => {
    2
    add('database', new Database())
    3
    })
  4. Now, when your bot starts, the init method will be called. 🎉

Dispose

Your object needs to destroy things before shutdown, if a crash occurs

  1. Do you need to perform intializing behavior for a dependency?

    src/database.js
    1
    import pg from 'pg'
    2
    const { Client } = pg
    3
    class Database {
    4
    __database = new Client()
    5
    async init() {
    6
    await this.__database.connect();
    7
    }
    8
    async dispose() {
    9
    console.log("Disposing database")
    10
    }
    11
    }
  2. Make sure its been added:

    src/index.ts
    1
    await makeDependencies(({ add }) => {
    2
    add('database', new Database())
    3
    })
  3. Now, when your bot starts, the dispose method will be called. 🎉

Usage in Commands

This is for command modules, plugins only. event modules would have to use the Service api

Your dependencies are located in SDT.

1
export default commandModule({
2
type: CommandType.Slash,
3
execute: (ctx, sdt) =>{
4
sdt.deps.database // Database
5
}
6
})

Service

The Service api is used for places where sern cannot inject into parameters properly. View

When to use Dependency injection

Leveraging dependency injection in your projects is recommended because DI creates a loose coupling between the parts of your application. As a result, the codebase is more modular and easier to test and refactor