Skip to content

nerox-llclient

A fast, typed Lavalink v4 client for production Discord music bots.
  • Smart node selection with region preference, load balancing, priority, health, or custom policies.
  • A typed player API with automatic queue progression, repeat modes, unresolved tracks, filters, lyrics, and plugin events.
  • Persistent queue stores and queue change watchers for multi-process bots.
  • Lavalink v4 and NodeLink support without forcing a framework or Discord library.
  • Clear errors, debug events, source validation, and safe link allowlists/denylists.

nerox-llclient

Fast, typed Lavalink v4 and NodeLink client for Discord music bots.

nerox-llclient gives you a small, framework-agnostic player API with smart node selection, resilient reconnects, persistent queues, filters, lyrics, SponsorBlock, and typed events. It works with discord.js, Oceanic, Eris, or a custom gateway client.

Why use it

  • Smart node selection — balance by players, active players, memory, CPU, REST calls, priority, health, voice region, or your own selector.
  • Resilient playback — automatic queue progression, reconnects, failover, autoplay hooks, repeat modes, and error thresholds.
  • Fast transitions — in-flight resolution deduplication, background prefetching, serialized queue persistence, and guarded track transitions.
  • Reliable sessions — stale websocket protection, healthy-handshake retry handling, and voice-region reconnect support for long-running bots.
  • Persistent queues — use the fast in-memory store or plug in Redis, PostgreSQL, a database, or any async store.
  • Lavalink-native — v4 REST/WebSocket support, plugin validation, SponsorBlock, lyrics, filters, and NodeLink features.
  • Easy to extend — custom player classes, queue watchers, requester transformers, search sources, link allowlists, and typed events.
  • No framework lock-in — the only Discord-specific callback is forwarding voice state payloads to your shard.

Requirements

  • Node.js 18 or newer, or Bun 1.1.27 or newer
  • Lavalink v4 or NodeLink
  • A Discord bot with the Guild Voice States intent

Install

Terminal window
npm install nerox-llclient
# or: pnpm add nerox-llclient
# or: yarn add nerox-llclient
# or: bun add nerox-llclient

Quick start

import { LavalinkManager } from "nerox-llclient";
const lavalink = new LavalinkManager({
nodes: [
{
id: "primary",
host: process.env.LAVALINK_HOST ?? "localhost",
port: Number(process.env.LAVALINK_PORT ?? 2333),
authorization: process.env.LAVALINK_PASSWORD ?? "youshallnotpass",
regions: ["asia", "us"],
priority: 10,
},
],
nodeSelection: {
strategy: "health",
},
sendToShard: (guildId, payload) => {
// Forward this payload through your Discord library.
discordClient.ws.send(guildId, payload);
},
client: {
id: process.env.CLIENT_ID!,
username: "my-music-bot",
},
playerOptions: {
onDisconnect: { autoReconnect: true, destroyPlayer: false },
onEmptyQueue: { destroyAfterMs: 30_000 },
},
});
discordClient.on("raw", (packet) => void lavalink.sendRawData(packet));
discordClient.once("ready", async () => {
await lavalink.init({
id: discordClient.user.id,
username: discordClient.user.username,
});
});

Create a player, search, queue, and play:

const player = lavalink.createPlayer({
guildId,
voiceChannelId,
textChannelId,
selfDeaf: true,
});
await player.connect();
const result = await player.search({ query: "Daft Punk Get Lucky" }, requester, true);
await player.queue.add(result.tracks.slice(0, 10));
await player.play();

The queue includes convenient size, isEmpty, and duration properties, plus filtering, shuffling, repeat modes, previous tracks, unresolved tracks, and persistence hooks.

Node selection

Use a manager default:

const lavalink = new LavalinkManager({
// ...
nodeSelection: {
strategy: "priority",
region: "asia",
},
});

Or select explicitly:

const node = lavalink.getBestNode({
strategy: "health",
region: "us",
});
const player = lavalink.createPlayer({ guildId, voiceChannelId, node });

Available built-in strategies:

StrategyBest for
playersGeneral-purpose balancing
playingPlayersKeeping active playback evenly distributed
memoryNodes with different memory capacity
cpuLavalink / cpuSystemCPU-sensitive deployments
callsDistributing REST traffic
priorityPrimary and backup nodes
healthMixed hardware and automatic health scoring
customApplication-specific policies

Queues and storage

The default queue store is in-memory. For multi-process bots, implement QueueStoreManager and pass it to queueOptions.queueStore. Queue changes can also be observed through queueOptions.queueChangesWatcher.

await player.queue.add(tracks);
await player.queue.remove(2);
await player.queue.shuffle();
const longSongs = player.queue.utils.filterTracks({
duration: { min: 5 * 60_000 },
});

Events

lavalink.on("trackStart", (player, track) => {
console.log(`${player.guildId}: ${track?.info.title}`);
});
lavalink.on("queueEnd", (player) => {
console.log(`Queue finished in ${player.guildId}`);
});
lavalink.on("debug", (key, data) => {
if (data.state === "error") console.error(key, data.error);
});

Development

Terminal window
npm install
npm run build
npm run lint

The package build outputs CommonJS, ESM, and declaration files in dist/. The documentation site is in docs/ and uses Astro Starlight with generated TypeDoc API pages. The testBot/ directory contains a full Discord example; it needs a Discord token and a reachable Lavalink server.

Documentation

License

MIT