Discord.js v13 introduces several major changes including a completely new caching system which enables users to fully customize the library to match their caching preferences, however not all caching configurations are officially supported and some may introduce several side effects.
This library aims to improve support and usability when using discord.js with limited or disabled caches.
- master - based on the discord.js master branch (not actively maintained, should not be used)
- v4 - current npm version, based on discord.js v13
- v3 - old npm version, based on discord.js v12
- v2 - deprecated
- v1 - deprecated
- Fully supports the new discord.js caching system, including unsupported configurations, with little to no side effects
- Discord.js partials system removed and replaced with an internal "always on" solution
- Events always work, regardless of caching options (partial structures are given when missing), see djs-cache-test
- Partials can be created on demand to interact with the Discord API without fetching first
- Additional utilities to improve usability with caches disabled
const Discord = require("discord.js-light");
const client = new Discord.Client({
// default caching options, feel free to copy and modify. more info on caching options below.
makeCache: Discord.Options.cacheWithLimits({
ApplicationCommandManager: 0, // guild.commands
BaseGuildEmojiManager: 0, // guild.emojis
ChannelManager: 0, // client.channels
GuildChannelManager: 0, // guild.channels
GuildBanManager: 0, // guild.bans
GuildInviteManager: 0, // guild.invites
GuildManager: Infinity, // client.guilds
GuildMemberManager: 0, // guild.members
GuildStickerManager: 0, // guild.stickers
GuildScheduledEventManager: 0, // guild.scheduledEvents
MessageManager: 0, // channel.messages
PermissionOverwriteManager: 0, // channel.permissionOverwrites
PresenceManager: 0, // guild.presences
ReactionManager: 0, // message.reactions
ReactionUserManager: 0, // reaction.users
RoleManager: 0, // guild.roles
StageInstanceManager: 0, // guild.stageInstances
ThreadManager: 0, // channel.threads
ThreadMemberManager: 0, // threadchannel.members
UserManager: 0, // client.users
VoiceStateManager: 0 // guild.voiceStates
}),
intents: [ /* your intents here */ ],
});
client.on("messageCreate", async message => {
if(!message.guild) return;
// if .partial is true, only .id is guaranteed to exist, all other properties will be either null or undefined, if you need them, you must fetch them from the api
const guildNameOrId = message.guild.partial ? message.guild.id : message.guild.name;
const channelNameOrId = message.channel.partial ? message.channel.id : message.channel.name;
await message.channel.send(`hello from guild ${guildNameOrId} and channel ${channelNameOrId}`);
});
client.login("your token here");
Discord.js's new caching configuration is very powerful, here are a few examples:
{
ChannelManager: 0, // cache disabled. nothing will ever be added, except when using .cache.forceSet(). items added with forceSet can be updated and deleted normally.
GuildChannelManager: { maxSize: 0 }, // same as above
RoleManager: 5, // only 5 items will be allowed, any new item will first remove the oldest by insertion order before being added.
UserManager: {
maxSize: 10,
/**
* if set, this function is called when a new item is added and the collection is already at the limit.
* the collection starts looking for the oldest item to remove and tests each item with this function.
* if the function returns true, the item is not removed, and the next is tested. The first item that returns false is removed, then the new item is inserted.
* If maxSize is 0 or if updating an existing item, this function is not called.
* This example will prevent the bot user from ever being removed due to the cache being full and some other user will be removed instead.
*/
keepOverLimit: (value, key, collection) => value.id === value.client.user.id
},
GuildMemberManager: {
maxSize: Infinity,
/**
* if set, automatic sweeping is enabled, and this function is called every sweepInterval seconds.
* this function provides the collection being swept, and expects another function as a return value.
* the returned function will be given to collection.sweep() internally, and will delete all items for which the function returns true.
* this example will remove all members except the bot member, every 600 seconds.
*/
/*
* As of discord.js version 13.4.0+ (discord.js-light 4.5.0+) sweepFilter and sweepInterval are deprecated.
* Instead use the new client sweeper options.
*/
sweepFilter: collection => (value, key, collection) => value.id !== value.client.user.id,
sweepInterval: 600 // autosweep interval in seconds
}
}
New sweeper options added with discord.js v13.4.0 (discord.js-light 4.5.0):
new Discord.Client({
makeCache: Discord.Options.cacheWithLimits({
UserManager: {
maxSize: 50,
keepOverLimit: (value, key, collection) => value.id === value.client.user.id
},
MessageManager: {
maxSize: 100
}
}),
sweepers: {
users: {
interval: 600,
filter: user => user.id !== user.client.user.id
},
messages: {
interval: 600,
filter: message => message.author?.id !== message.client.user.id
}
}
})
For further information refer to the official discord.js documentation.
All managers implement this method to create partial versions of uncached objects on demand. This enables making API requests without fetching uncached objects first. This is only used to send requests to the API, if you need to access the object's properties, you need to fetch it. Some forge methods require additional parameters, check your intelisense.
await client.users.forge(id).send("hello");
All caches implement this method, which is the same as .cache.set()
but works even if the caches are completely disabled (set to 0). Use this to manually cache fetched items.
const user = await client.users.fetch(id);
client.users.cache.forceSet(id, user);
Method added to improve accessibility to permission checking when caches are disabled. This can be used to work with permissions directly but to use regular permission checking methods, they need to be manually added to the cache.
const overwrites = await channel.fetchOverwrites();
console.log(overwrites) // Collection<PermissionOverwrites>
// to enable permission checking in this channel if permissionOverwrites are not cached
overwrites.forEach(overwrite => {
channel.permissionOverwrites.cache.forceSet(overwrite.id, overwrite);
})
Event fired when each individual internal shard connects.
client.on("shardConnect", (shardId, guilds) => {
console.log(shardId) // shard ID
console.log(guilds) // array of unavailable guilds as per the Discord API
});
Event fired instead of the standard emoji events when the emoji cache is disabled. If the emojis cache is disabled, emojiCreate, emojiUpdate and emojiDelete will never be fired.
client.on("guildEmojisUpdate", emojis => {
console.log(emojis) // Collection<GuildEmoji>
})
Event fired instead of the standard sticker events when the sticker cache is disabled. If the stickers cache is disabled, stickerCreate, stickerUpdate and stickerDelete will never be fired.
client.on("guildStickersUpdate", stickers => {
console.log(stickers) // Collection<Sticker>
})
Event fired when the library makes a request to the discord API. Use this to debug rate limit issues.
client.on("rest", request => {
console.log(request); /*
{
path: string,
method: string,
responseHeaders: object,
responseBody: string
} */
});
Fetching data does not automatically cache if cache limits are set to 0. Use the non-standard Collection#forceSet
method instead to manually cache fetched items. Manually cached items can be accessed, updated, swept and removed normally.
The bot member is cached by default (unless removed by the user). GuildMemberManager auto-sweep will still remove it if not excluded in your sweepFilter.
The everyone role is cached by default (unless removed by the user). RoleManager auto-sweep will still remove it if not excluded in your sweepFilter.
ChannelManager and GuildChannelManager should be configured together, otherwise weird things can happen if they have different configurations, use at your own risk. If anything prioritize enabling ChannelManager over GuildChannelManager.
Note about continued development: v4 will likely be the last discord.js-light version. As of v13, discord.js is now much better than what it used to be in terms of configurability and resource management, and v14 will likely be even better, to the point where discord.js-light probably wont be useful anymore. For now v4 will still be maintained and will still keep up with discord.js's v13 development as needed, but this adventure might reach a conclusion soon. So long and thanks for all the fish!
An example for maximizing cache efficiency while keeping full support for permission checking. Using the new autosweep functionality and the non-standard forceSet methods to keep only active text channels cached.
const Discord = require("discord.js-light");
// remove non-text channels and remove text channels whose last message is older than 1 hour
function channelFilter(channel) {
return !channel.lastMessageId || Discord.SnowflakeUtil.timestampFrom(channel.lastMessageId) < Date.now() - 3600000;
}
const makeCache = Discord.Options.cacheWithLimits({
GuildManager: Infinity, // roles require guilds
RoleManager: Infinity, // cache all roles
PermissionOverwrites: Infinity, // cache all PermissionOverwrites. It only costs memory if the channel it belongs to is cached
ChannelManager: {
maxSize: 0, // prevent automatic caching
sweepFilter: () => channelFilter, // remove manually cached channels according to the filter
sweepInterval: 3600
},
GuildChannelManager: {
maxSize: 0, // prevent automatic caching
sweepFilter: () => channelFilter, // remove manually cached channels according to the filter
sweepInterval: 3600
},
/* other caches */
});
const client = new Discord.Client({ makeCache, intents: [ /* your intents */ ] });
client.on("messageCreate", async message => {
// if the channel is not cached, manually cache it while its active
if(!client.channels.cache.has(message.channel.id)) {
const channel = await client.channels.fetch(message.channel.id);
client.channels.cache.forceSet(channel.id, channel); // manually cache it in client.channels
message.guild?.channels.cache.forceSet(channel.id, channel); // manually cache it in guild.channels
}
console.log(message.channel.permissionsFor(message.member));
});
An example for caching roles only for the bot itself and ignoring all other roles. Use in combination with the previous example if you want to check permissions in channels as well.
const Discord = require("discord.js-light");
const makeCache = Discord.Options.cacheWithLimits({
GuildManager: Infinity, // roles require guilds
RoleManager: {
maxSize: Infinity, // start with cache enabled to get initial roles, then disable it in the ready event
sweepFilter: () => role => !role.guild.me.roles.has(role.id), // remove roles the bot doesnt have
sweepInterval: 3600
},
/* other caches */
});
const client = new Discord.Client({ makeCache, intents: [ /* your intents */ ] });
client.on("ready", () => {
client.guilds.forEach(guild => {
// disable cache and remove roles we dont have
guild.roles.cache.maxSize = 0;
guild.roles.cache.sweep(role => !guild.me.roles.has(role.id))
});
});
// this event is still sent for the bot user even if you dont have the GUILD_MEMBERS intent
client.on("guildMemberUpdate", async (oldMember, newMember) => {
if(newMember.id === client.user.id) {
// check for new roles and fetch them as needed
// removed roles will be uncached later by the autosweeper
for(const role of newMember.roles.cache.values()) {
if(role.partial) {
const fetched = await newMember.guild.roles.fetch(role.id);
newMember.guild.roles.cache.forceSet(role.id, fetched);
}
}
}
});