Skip to main content

Using and extending container

The container is a way in which Sapphire achieves Dependency Injection. This is a very useful feature, since it allows accessing the client, the stores and various other properties from anywhere in your code.

Using container

The container can be used to access your custom properties, or the ones attached by the framework (like the client). Suppose you want to access the client in a file where you don't extend a sapphire piece (like commands, listeners, etc.), meaning you can't access container from this, you can manually import the container from the framework and access your client:

const { container } = require('@sapphire/framework');

class ExampleClass {
run() {
const { client } = container;
// rest of your code
}
}
module.exports = {
ExampleClass
};

Extending container

You can attach your custom properties to the container, as mentioned above, to access them in your code:

const { container } = require('@sapphire/framework');

container.property = 'value';

console.log(container.property); // value

The above code attaches property to the container with the value value, and then augments the @sapphire/pieces module to add property to the container interface, this is a simple example to demonstrate how you can attach properties to the container.

Practical example

info

The examples above have shown very basic implementations of how to extend the container, but it is also often valuable to see a more practical example. In the following code block we attach a database connection to the container after logging in with the SapphireClient. This way the database connection can be accessed anything, through the container.

const { isGuildBasedChannel } = require('@sapphire/discord.js-utilities');
const { container, SapphireClient } = require('@sapphire/framework');
const { GatewayIntentBits } = require('discord.js');

// First we extend SapphireClient
class MyCustomClient extends SapphireClient {
constructor() {
// We call super our options
super({
caseInsensitiveCommands: true,
caseInsensitivePrefixes: true,
defaultPrefix: '!',
intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages],
loadDefaultErrorListeners: false
});
}

// We override login to connect to our database and then login to Discord
async login(token) {
container.database = await createMyDatabaseConnection();
return super.login(token);
}

// We override destroy to kill the connection to our database before logging out at Discord
async destroy() {
await container.database.destroy();
return super.destroy();
}

/**
* Retrieves the prefix for the guild, or if the command used in a guild then default prefix.
* @param message The message that gives context.
*/
fetchPrefix = async (message) => {
if (isGuildBasedChannel(message.channel)) {
// We can now use the "container.database" to retrieve the prefix for the guild
return container.database.read('guilds', message.guild.id, 'prefix');
}

return [this.options.defaultPrefix, ''];
};
}
module.exports = {
MyCustomClient
};