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
};