Skip to main content
A RestAction is JDA’s way of handling asynchronous API requests. It provides a terminal interface between the user and Discord’s API, allowing you to control how and when requests are executed.

Overview

RestAction provides three main execution methods:
  • queue() - Asynchronous execution with callbacks (recommended)
  • submit() - Returns a CompletableFuture for the result
  • complete() - Blocks the current thread until completion

Execution Methods

queue

Submits the request for asynchronous execution.
success
Consumer<? super T>
Callback executed on success
failure
Consumer<? super Throwable>
Callback executed on failure
Variants:
  • queue() - No callbacks, uses defaults
  • queue(Consumer<T> success) - Success callback only
  • queue(Consumer<T> success, Consumer<Throwable> failure) - Both callbacks
// Simple queue with no callback
channel.sendMessage("Hello!").queue();

// With success callback
channel.sendMessage("Hello!").queue(message -> {
    System.out.println("Message sent: " + message.getId());
});

// With both callbacks
user.openPrivateChannel().queue(
    channel -> channel.sendMessage("DM sent!").queue(),
    error -> System.err.println("Failed to open DM: " + error.getMessage())
);

submit

Submits the request and returns a CompletableFuture.
shouldQueue
boolean
default:"true"
Whether to automatically handle rate limits
return
CompletableFuture<T>
Future that completes with the response value
CompletableFuture<Message> future = channel.sendMessage("Hello!").submit();

future.thenAccept(message -> {
    System.out.println("Message sent: " + message.getId());
}).exceptionally(error -> {
    error.printStackTrace();
    return null;
});

// Chain multiple requests
jda.retrieveUserById(userId).submit()
    .thenCompose(user -> user.openPrivateChannel().submit())
    .thenCompose(channel -> channel.sendMessage("Hello!").submit())
    .whenComplete((msg, error) -> {
        if (error != null) error.printStackTrace();
    });

complete

Blocks the current thread until the request completes.
shouldQueue
boolean
default:"true"
Whether to automatically handle rate limits
return
T
The response value
Not recommended for most use cases. Blocking threads can cause performance issues. Use queue() or submit() instead.
// Block until message is sent
Message message = channel.sendMessage("Hello!").complete();
System.out.println("Sent: " + message.getId());

// This blocks the thread!
User user = jda.retrieveUserById(userId).complete();

Transformation Operators

map

Transforms the result to a different type.
map
Function<? super T, ? extends O>
required
Mapping function to apply
return
RestAction<O>
RestAction with the mapped type
// Get user's name from ID
RestAction<String> nameAction = jda.retrieveUserById(userId)
    .map(User::getName);

nameAction.queue(name -> {
    System.out.println("User name: " + name);
});

// Get message content length
channel.sendMessage("Hello")
    .map(Message::getContentRaw)
    .map(String::length)
    .queue(length -> System.out.println("Length: " + length));

flatMap

Chains another RestAction based on the result.
flatMap
Function<? super T, ? extends RestAction<O>>
required
Function that returns the next RestAction
return
RestAction<O>
RestAction for the chained operation
// Send a DM to a user
jda.retrieveUserById(userId)
    .flatMap(User::openPrivateChannel)
    .flatMap(channel -> channel.sendMessage("Hello!"))
    .queue(message -> System.out.println("DM sent!"));

// Conditional flatMap
channel.retrieveMessageById(messageId)
    .flatMap(
        msg -> msg.getAuthor().isBot(), // condition
        msg -> msg.delete() // only if condition is true
    )
    .queue();

onSuccess

Executes a consumer on success and continues the chain.
consumer
Consumer<? super T>
required
Consumer to execute on success
return
RestAction<T>
RestAction that consumes but preserves the result
channel.sendMessage("Hello!")
    .onSuccess(msg -> System.out.println("Sent: " + msg.getId()))
    .flatMap(msg -> msg.addReaction("✅"))
    .queue();

Error Handling

onErrorMap

Provides a fallback value on failure.
condition
Predicate<? super Throwable>
Optional condition to check before applying fallback
map
Function<? super Throwable, ? extends T>
required
Function to provide fallback value
return
RestAction<T>
RestAction with fallback handling
import static net.dv8tion.jda.api.requests.ErrorResponse.*;

// Provide default value on any error
jda.retrieveUserById(userId)
    .map(User::getName)
    .onErrorMap(error -> "Unknown User")
    .queue(name -> System.out.println("Name: " + name));

// Conditional fallback
user.openPrivateChannel()
    .flatMap(channel -> channel.sendMessage("Hello"))
    .onErrorMap(
        CANNOT_SEND_TO_USER::test,
        error -> null
    )
    .queue();

onErrorFlatMap

Provides a fallback RestAction on failure.
condition
Predicate<? super Throwable>
Optional condition to check before applying fallback
map
Function<? super Throwable, ? extends RestAction<? extends T>>
required
Function to provide fallback action
return
RestAction<T>
RestAction with fallback action
import static net.dv8tion.jda.api.requests.ErrorResponse.*;

// Try DM, fallback to channel message
user.openPrivateChannel()
    .flatMap(channel -> channel.sendMessage("Hello!"))
    .onErrorFlatMap(
        CANNOT_SEND_TO_USER::test,
        error -> textChannel.sendMessage(user.getAsMention() + " Hello!")
    )
    .queue();

mapToResult

Wraps success and failure in a Result object.
return
RestAction<Result<T>>
RestAction that never fails, wrapping errors in Result
RestAction<Result<User>> result = jda.retrieveUserById(userId)
    .mapToResult();

result.queue(r -> {
    if (r.isSuccess()) {
        System.out.println("User: " + r.get().getName());
    } else {
        System.out.println("Failed: " + r.getFailure());
    }
});

Combining Actions

and

Combines two RestActions and computes a result from both.
other
RestAction<U>
required
The other action to combine
accumulator
BiFunction<? super T, ? super U, ? extends O>
Function to combine results (optional)
return
RestAction<O>
Combined RestAction
RestAction<User> userAction = jda.retrieveUserById(userId);
RestAction<Guild> guildAction = jda.retrieveGuildById(guildId);

// Combine both results
userAction.and(guildAction, (user, guild) -> 
    user.getName() + " in " + guild.getName()
).queue(result -> System.out.println(result));

// Just wait for both to complete
userAction.and(guildAction).queue();

allOf

Accumulates multiple RestActions into a list.
first
RestAction<? extends E>
required
First action
others
RestAction<? extends E>...
Additional actions
return
RestAction<List<E>>
RestAction containing list of all results
RestAction<List<User>> allUsers = RestAction.allOf(
    jda.retrieveUserById(id1),
    jda.retrieveUserById(id2),
    jda.retrieveUserById(id3)
);

allUsers.queue(users -> {
    users.forEach(user -> System.out.println(user.getName()));
});

zip

Combines this action with others into a list.
first
RestAction<? extends T>
required
First action to zip
other
RestAction<? extends T>...
Additional actions to zip
return
RestAction<List<T>>
RestAction containing list of all results
RestAction<Message> msg1 = channel.sendMessage("First");
RestAction<List<Message>> all = msg1.zip(
    channel.sendMessage("Second"),
    channel.sendMessage("Third")
);

all.queue(messages -> {
    System.out.println("Sent " + messages.size() + " messages");
});

Delay Operations

delay

Delays the result by a specified duration.
duration
Duration | long
required
The delay duration
unit
TimeUnit
Time unit for numeric delay
scheduler
ScheduledExecutorService
Custom scheduler (null for default)
return
RestAction<T>
RestAction with delay
// Self-destructing message
channel.sendMessage("This will be deleted in 5 seconds")
    .delay(Duration.ofSeconds(5))
    .flatMap(Message::delete)
    .queue();

// Chain delays
channel.sendMessage("Step 1")
    .delay(2, TimeUnit.SECONDS)
    .flatMap(msg -> msg.editMessage("Step 2"))
    .delay(2, TimeUnit.SECONDS)
    .flatMap(msg -> msg.editMessage("Step 3"))
    .queue();

queueAfter

Schedules the action to execute after a delay.
delay
long
required
The delay value
unit
TimeUnit
required
Time unit for the delay
success
Consumer<? super T>
Success callback
failure
Consumer<? super Throwable>
Failure callback
executor
ScheduledExecutorService
Custom executor
return
ScheduledFuture<?>
Future for the scheduled operation
// Delete after 10 seconds
message.delete().queueAfter(10, TimeUnit.SECONDS);

// With callback
channel.sendMessage("Delayed").queueAfter(
    5,
    TimeUnit.SECONDS,
    msg -> System.out.println("Sent after delay")
);

submitAfter

Schedules the action and returns a CompletableFuture.
delay
long
required
The delay value
unit
TimeUnit
required
Time unit for the delay
executor
ScheduledExecutorService
Custom executor
return
DelayedCompletableFuture<T>
Future for the delayed operation
DelayedCompletableFuture<Message> future = 
    channel.sendMessage("Delayed").submitAfter(5, TimeUnit.SECONDS);

future.thenAccept(msg -> System.out.println("Sent!"));

completeAfter

Blocks for the delay then executes the action.
delay
long
required
The delay value
unit
TimeUnit
required
Time unit for the delay
return
T
The response value
Blocks the current thread. Not recommended for most use cases.

Checks and Timeouts

setCheck

Sets a last-second check before execution.
checks
BooleanSupplier
Checks to run before executing (null = no checks)
return
RestAction<T>
RestAction with checks
Message toDelete = /* ... */;

toDelete.delete()
    .setCheck(() -> !toDelete.isPinned()) // Only delete if not pinned
    .queue();

addCheck

Adds additional checks to existing checks.
checks
BooleanSupplier
required
Additional checks to add
return
RestAction<T>
RestAction with added checks
action.setCheck(() -> condition1())
    .addCheck(() -> condition2())
    .queue();

timeout

Sets a timeout for the action.
timeout
long
required
Timeout value
unit
TimeUnit
required
Time unit for timeout
return
RestAction<T>
RestAction with timeout
jda.retrieveUserById(userId)
    .timeout(5, TimeUnit.SECONDS)
    .queue(
        user -> System.out.println("Found: " + user.getName()),
        error -> System.err.println("Timed out or failed")
    );

deadline

Sets an absolute deadline timestamp.
timestamp
long
required
Millisecond timestamp for deadline
return
RestAction<T>
RestAction with deadline
long deadline = System.currentTimeMillis() + 10000; // 10 seconds from now

action.deadline(deadline)
    .queueAfter(20, TimeUnit.SECONDS); // Will timeout after 10 seconds

Static Configuration

setDefaultFailure

Sets the default failure callback for all RestActions.
callback
Consumer<? super Throwable>
The default failure handler (null to ignore)
RestAction.setDefaultFailure(error -> {
    System.err.println("Request failed: " + error.getMessage());
});

setDefaultSuccess

Sets the default success callback for all RestActions.
callback
Consumer<Object>
The default success handler (null to ignore)
RestAction.setDefaultSuccess(result -> {
    System.out.println("Request completed");
});

setDefaultTimeout

Sets the default timeout for all RestActions.
timeout
long
required
Timeout value (0 = no timeout)
unit
TimeUnit
required
Time unit for timeout
RestAction.setDefaultTimeout(30, TimeUnit.SECONDS);

setPassContext

Enables context tracking for better error messages.
enable
boolean
required
Whether to track context
May impact performance. Creates stack traces for every execution.
RestAction.setPassContext(true);

Best Practices

Use queue() for Async Operations

// Good - non-blocking
channel.sendMessage("Hello").queue();

// Bad - blocks the thread
channel.sendMessage("Hello").complete();

Chain Operations with flatMap

// Good - clean chain
jda.retrieveUserById(userId)
    .flatMap(User::openPrivateChannel)
    .flatMap(channel -> channel.sendMessage("Hello"))
    .queue();

// Bad - nested callbacks
jda.retrieveUserById(userId).queue(user -> {
    user.openPrivateChannel().queue(channel -> {
        channel.sendMessage("Hello").queue();
    });
});

Handle Errors Gracefully

user.openPrivateChannel()
    .flatMap(channel -> channel.sendMessage("Hello"))
    .queue(
        message -> System.out.println("DM sent"),
        error -> System.err.println("Failed to send DM: " + error.getMessage())
    );

Build docs developers (and LLMs) love