Skip to content

REST

REST is Lunibee’s HTTP client for the Discord REST API. It handles per-route rate limiting, global rate limits, exponential backoff, and cancellation via AbortSignal.

import { REST, Routes } from "lunibee";
// or directly:
import { REST, Routes } from "@lunibee/rest";
new REST(options: RESTOptions)
Option Type Description
token string Discord bot token.
apiVersion number API version (default: 10).
baseURL string Override the Discord base URL.
const rest = new REST({ token: process.env.DISCORD_TOKEN! });

All methods accept an optional RESTRequestOptions object as the last argument.

const user = await rest.get<APIUser>(Routes.user("@me"));

Accepts the body directly or as { body: ... } (Discord.js-familiar form):

// Direct body (Lunibee canonical):
const message = await rest.post(Routes.channelMessages(channelId), { content: "Hello!" });
// Discord.js-familiar:
const message = await rest.post(Routes.channelMessages(channelId), { body: { content: "Hello!" } });
await rest.patch(Routes.channel(channelId), { name: "new-name" });
await rest.put(Routes.guildBan(guildId, userId), { delete_message_seconds: 86400 });
await rest.delete(Routes.message(channelId, messageId));

postWithFiles<T>(path, body, files, options?)

Section titled “postWithFiles<T>(path, body, files, options?)”

Sends a multipart/form-data request with file attachments.

await rest.postWithFiles(
Routes.channelMessages(channelId),
{ content: "Here is your file!" },
[{ name: "report.csv", data: csvBuffer, contentType: "text/csv" }],
);

Updates the token used for subsequent requests.

Routes provides strongly-typed path builders for every Discord API endpoint:

Routes.user("@me") // "/users/@me"
Routes.channel(channelId) // "/channels/{id}"
Routes.channelMessages(channelId) // "/channels/{id}/messages"
Routes.message(channelId, messageId) // "/channels/{id}/messages/{messageId}"
Routes.guild(guildId) // "/guilds/{id}"
Routes.guildChannels(guildId) // "/guilds/{id}/channels"
Routes.guildMember(guildId, userId) // "/guilds/{id}/members/{userId}"
Routes.guildBan(guildId, userId) // "/guilds/{id}/bans/{userId}"
Routes.guildRoles(guildId) // "/guilds/{id}/roles"
Routes.interactionCallback(id, token) // "/interactions/{id}/{token}/callback"
const controller = new AbortController();
setTimeout(() => controller.abort(), 5000); // cancel after 5s
try {
const data = await rest.get(Routes.guild(guildId), { signal: controller.signal });
} catch (error) {
if (error instanceof RESTError && error.name === "AbortError") {
console.warn("Request was cancelled.");
}
}

Thrown when Discord returns a non-2xx response.

import { RESTError } from "lunibee";
try {
await rest.delete(Routes.message(channelId, messageId));
} catch (error) {
if (error instanceof RESTError) {
console.error(`Discord API Error ${error.status}: ${error.message}`);
console.error("Code:", error.code); // Discord error code
console.error("Method:", error.method); // "DELETE"
console.error("Path:", error.path); // "/channels/.../messages/..."
}
}

Fetch a user and send a DM

import { REST, Routes } from "lunibee";
const rest = new REST({ token: process.env.DISCORD_TOKEN! });
// Fetch a user
const user = await rest.get(Routes.user("123456789012345678"));
// Open a DM channel
const dmChannel = await rest.post(Routes.userChannels(), { recipient_id: "123456789012345678" });
// Send a message
await rest.post(Routes.channelMessages((dmChannel as any).id), {
content: "Hello from Lunibee REST! 🐝",
});