Discolink
A Lavalink client with load-aware queue distribution, regional voice latency routing, and automatic node failover for Discord bots in TypeScript
Install / Use
/learn @execaman/DiscolinkREADME
Icon by juicy_fish <br/> API Reference | Coverage
🎯 Purpose
The goal of this library is to abstract away obvious steps involved in the process of acting as an intermediary between Lavalink and Discord to give developers a cleaner and intuitive interface to work with.
🙌 Motivation
It's the JS ecosystem, how can we not have 30 libs for the same thing. My friends were monkey-patching, applying hotfixes, despite their clients being open-source; and I wanted to do a project professionally while exploring more of GitHub. This project follows SemVer and an Agile SDLC.
⚙️ Requirements
- Runtime - one of the following:
- Library - any gateway client that supports:
- sending raw payloads over the connection
- receiving raw payloads from the connection
📝 Implementation
Examples
<details> <summary>Basic Setup - JavaScript (ESM)</summary>import { Client } from "main-lib";
import { Player } from "discolink";
const client = new Client(...);
const player = new Player({
nodes: [ // add your nodes
{
name: "local",
origin: "http://localhost:2333",
password: "youshallnotpass"
}
],
async forwardVoiceUpdate(guildId, payload) {
// send the given payload to your gateway connection
client.guilds.cache.get(guildId).shard.send(payload);
}
});
client.on("raw", (payload) => {
// call the handler on gateway dispatch
player.voices.handleDispatch(payload);
});
client.login();
</details>
<details>
<summary>Module Augmentation - TypeScript</summary>
/**
* fields defined here appear wherever they're concerned
*/
declare module "discolink" {
// appears on queue, related options, etc
interface QueueContext {
textId: string;
}
// appears on track, related options, etc
interface CommonUserData {
id: string;
username: string;
displayName: string;
}
// appears on track, playlist, etc
interface CommonPluginInfo {
save_uri?: string;
}
// appears throughout filter management
interface CommonPluginFilters {
custom: string;
}
}
</details>
<details>
<summary>Custom Plugin (with events) - TypeScript</summary>
import { PlayerPlugin, type Player } from "discolink";
export class CustomPlugin extends PlayerPlugin<{
// define events you want to emit on player
eventName: [s: number, d: object];
}> {
readonly name = "custom"; // 'readonly' is mandatory
#player!: Player; // optional, just for convenience
init(player: Player) {
this.#player = player;
player.on("nodeDispatch", this.#onDispatch);
}
transform(...args: unknown[]): [s: number, d: object] {}
#onDispatch(this: Player, ...args: unknown[]) {
// work with data
// e.g. transform -> rename event -> dispatch
const transformed = this.transform(...args);
this.emit("eventName", ...transformed);
}
}
</details>
Additional Notes
-
Destroy queues when necessary, e.g. events like guild/channel delete, etc.
-
Check voice states like
reconnectingandchangingNodebefore taking action -
Handle track end reasons other than
cleanupandfinished- especiallyreplaced
[!NOTE]
replacedis an edge case where we cannot reliably determine the exact track object in queue that ended. The queue implements a workaround for this and provides ainQueue(think cache hit/miss) boolean in track events
Session Resumption
Resuming a node's session after your bot restarts requires careful planning, depending on scale. As such, the lib has no plans to provide built-in support for it. Disable either or both of autoSync and relocateQueues options for predictable behavior if you're implementing this feature.
🤖 Bots in Production
| Name | Since | Owner |
| ----------------------------------------------------------------------------- | ------------- | ---------------------------------------------------------------- |
| Mesuic | 18th Feb 2026 | @knifecodez |
| Fuego | 19th Feb 2026 | @painfuego |
| Flame | 28th Feb 2026 | @aiosqlite.db |
| Bumblebee | 2nd Apr 2026 | @freycikkk |
🤝 Acknowledgements
Key aspects of this lib were inspired from the following projects:
distubeplayer-queue designdiscord.jsmanager-cache conceptHoshimimodule augmentation (typings)
