The Supabase UE5 Plugin provides comprehensive query operations for database interaction through production-ready async nodes and Blueprint-friendly interfaces. All query operations are built with memory-safe, thread-safe patterns and integrate seamlessly with the SupabaseSubsystem for optimal performance.
The main async node for database queries with full Blueprint support and proper memory management.
Advanced filtering system supporting complex WHERE conditions, sorting, and pagination.
Static utility class providing simplified access to query operations.
Blueprint:

C++:
UAsyncQuery* QueryNode = UAsyncQuery::QueryTableAsync(
this,
TEXT("users"),
TEXT("*")
);
QueryNode->OnSuccess.AddDynamic(this, &AMyClass::OnQuerySuccess);
QueryNode->OnFailure.AddDynamic(this, &AMyClass::OnQueryFailure);
Blueprint:

C++:
UQueryFilter* Filter = USupabaseUtils::MakeQueryFilter();
Filter->AddFilter(TEXT("active"), EFilterOperator::Equals, TEXT("true"));
Filter->SetLimitFilter(10);
Filter->SetSortFilter(TEXT("created_at"), false); // DESC
UAsyncQuery* QueryNode = UAsyncQuery::QueryTableAsync(
this,
TEXT("users"),
TEXT("id,name,email"),
Filter
);
Fetches a single row from a table, returning the result as a FSQLResultRow (a map of FString key-value pairs).

static UAsyncFetchRow* FetchRowAsync(
UObject* WorldContextObject,
const FString& Table,
const FString& RowID,
ESupabaseKey KeyType = ESupabaseKey::Service
);
C++ Example:
UAsyncFetchRow* FetchNode = UAsyncFetchRow::FetchRowAsync(
this,
TEXT("users"),
TEXT("123")
);
FetchNode->OnSuccess.AddDynamic(this, &AMyClass::OnFetchSuccess);
FetchNode->OnFailure.AddDynamic(this, &AMyClass::OnFetchFailure);
FSQLResultRow provides the row data as TMap<FString, FString> via its Data member, making it easy to access individual column values.
The plugin provides a structured query filter system for building PostgREST-compatible query strings.
UENUM(BlueprintType)
enum class EFilterOperator : uint8
{
Equals, // Field = value
NotEquals, // Field != value
GreaterThan, // Field > value
LessThan, // Field < value
Like // Field LIKE pattern
};
USTRUCT(BlueprintType)
struct FQueryFilterProperties
{
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FString FieldName;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
EFilterOperator Operator;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FString Value;
};
The UQueryFilter class builds query strings from filter properties, sort directives, and limit values.
UCLASS()
class SUPABASE_API UQueryFilter : public UObject
{
GENERATED_BODY()
public:
// Add a WHERE condition
UFUNCTION(BlueprintCallable)
void AddFilter(const FString& FieldName, EFilterOperator Operator, const FString& Value);
// Set ORDER BY clause
UFUNCTION(BlueprintCallable)
void SetSortFilter(const FString& SortField, bool bAscending = true);
// Set LIMIT clause
UFUNCTION(BlueprintCallable)
void SetLimitFilter(int32 Limit);
// Generate the full query string
UFUNCTION(BlueprintCallable)
FString ToQueryString() const;
};
C++ Example:
UQueryFilter* Filter = USupabaseUtils::MakeQueryFilter();
Filter->AddFilter(TEXT("status"), EFilterOperator::Equals, TEXT("active"));
Filter->AddFilter(TEXT("score"), EFilterOperator::GreaterThan, TEXT("100"));
Filter->SetSortFilter(TEXT("created_at"), false); // DESC
Filter->SetLimitFilter(25);
FString QueryString = Filter->ToQueryString();
// Example output: "status=eq.active&score=gt.100&order=created_at.desc&limit=25"
Select Specific Columns:
// Select only specific fields
UAsyncQuery::QueryTableAsync(this, TEXT("users"), TEXT("id,name,email"));
// Select with relationships (JSON expansion)
UAsyncQuery::QueryTableAsync(this, TEXT("users"), TEXT("*,profiles(*)"));
// Select with computed fields
UAsyncQuery::QueryTableAsync(this, TEXT("sales"), TEXT("*,total:price.multiply(quantity)"));
The QueryFilter supports multiple filter operators:
| Operator | Description | Example |
|---|---|---|
Equals |
Exact match | status = 'active' |
NotEquals |
Not equal | status != 'deleted' |
GreaterThan |
Greater than | age > 18 |
LessThan |
Less than | price < 100 |
Like |
Pattern matching | name LIKE '%john%' |
C++ Example:
UQueryFilter* Filter = USupabaseUtils::MakeQueryFilter();
// Multiple conditions (AND logic)
Filter->AddFilter(TEXT("status"), EFilterOperator::Equals, TEXT("active"));
Filter->AddFilter(TEXT("age"), EFilterOperator::GreaterThan, TEXT("18"));
Filter->AddFilter(TEXT("city"), EFilterOperator::Like, TEXT("%Berlin%"));
// Sorting and pagination
Filter->SetSortFilter(TEXT("created_at"), false); // DESC
Filter->SetLimitFilter(50);
Range Queries:
// Date range example
UQueryFilter* Filter = USupabaseUtils::MakeQueryFilter();
Filter->AddFilter(TEXT("created_at"), EFilterOperator::GreaterThan, TEXT("2024-01-01"));
Filter->AddFilter(TEXT("created_at"), EFilterOperator::LessThan, TEXT("2024-12-31"));
Text Search:
// Full-text search using LIKE
UQueryFilter* Filter = USupabaseUtils::MakeQueryFilter();
Filter->AddFilter(TEXT("description"), EFilterOperator::Like, TEXT("%game%"));
Uses subsystem by default - recommended for production.
static UAsyncQuery* QueryTableAsync(
UObject* WorldContextObject,
const FString& Table,
const FString& Columns,
UQueryFilter* QueryFilter = nullptr,
ESupabaseKey KeyType = ESupabaseKey::Service
);
For advanced use cases requiring specific connection control.
static UAsyncQuery* QueryTableAdvanced(
UObject* WorldContextObject,
USupabaseConnection* Connection,
const FString& Table,
const FString& Columns,
UQueryFilter* QueryFilter = nullptr,
ESupabaseKey KeyType = ESupabaseKey::Service
);
UFUNCTION()
void OnQueryFailure(FString Error)
{
if (USupabaseManager::IsNetworkError(Error))
{
// Handle network connectivity issues
RetryQuery();
}
else if (USupabaseManager::IsAuthenticationError(Error))
{
// Handle authentication problems
ReauthenticateUser();
}
else
{
// Handle other errors (syntax, permissions, etc.)
UE_LOG(LogTemp, Error, TEXT("Query failed: %s"), *Error);
}
}
The plugin automatically uses the SupabaseSubsystem for:
// ❌ Avoid selecting all columns for large tables
UAsyncQuery::QueryTableAsync(this, TEXT("large_table"), TEXT("*"));
// ✅ Select only needed columns
UAsyncQuery::QueryTableAsync(this, TEXT("large_table"), TEXT("id,name,status"));
UQueryFilter* Filter = USupabaseUtils::MakeQueryFilter();
Filter->SetLimitFilter(25); // Reasonable page size
Filter->SetSortFilter(TEXT("id"), true); // Consistent ordering
// Store query results in local cache
UPROPERTY()
TMap<FString, FJsonObjectWrapper> QueryCache;
void AGameMode::LoadActiveUsers()
{
UQueryFilter* Filter = USupabaseUtils::MakeQueryFilter();
Filter->AddFilter(TEXT("is_active"), EFilterOperator::Equals, TEXT("true"));
Filter->AddFilter(TEXT("last_login"), EFilterOperator::GreaterThan, TEXT("2024-01-01"));
Filter->SetSortFilter(TEXT("last_login"), false); // Most recent first
Filter->SetLimitFilter(100);
UAsyncQuery* Query = UAsyncQuery::QueryTableAsync(
this,
TEXT("users"),
TEXT("id,username,email,last_login,level"),
Filter
);
Query->OnSuccess.AddDynamic(this, &AGameMode::OnUsersLoaded);
Query->OnFailure.AddDynamic(this, &AGameMode::OnUsersLoadFailed);
}
void ALeaderboardWidget::LoadTopScores()
{
UQueryFilter* Filter = USupabaseUtils::MakeQueryFilter();
Filter->SetSortFilter(TEXT("score"), false); // Highest scores first
Filter->SetLimitFilter(10); // Top 10 only
UAsyncQuery* Query = UAsyncQuery::QueryTableAsync(
this,
TEXT("leaderboard"),
TEXT("player_name,score,achieved_at"),
Filter
);
Query->OnSuccess.AddDynamic(this, &ALeaderboardWidget::DisplayScores);
}
void AInventorySystem::SearchItems(const FString& SearchTerm)
{
UQueryFilter* Filter = USupabaseUtils::MakeQueryFilter();
Filter->AddFilter(TEXT("name"), EFilterOperator::Like, FString::Printf(TEXT("%%%s%%"), *SearchTerm));
Filter->AddFilter(TEXT("is_available"), EFilterOperator::Equals, TEXT("true"));
Filter->SetSortFilter(TEXT("rarity"), false); // Rarest first
UAsyncQuery* Query = UAsyncQuery::QueryTableAsync(
this,
TEXT("items"),
TEXT("id,name,description,rarity,price"),
Filter
);
Query->OnSuccess.AddDynamic(this, &AInventorySystem::OnItemsFound);
}
The AsyncQuery node provides automatic memory management:
virtual void BeginDestroy() override
{
SafeCleanup();
Super::BeginDestroy();
}
void SafeCleanup()
{
CancelPendingRequest();
UnbindEvents();
// Clear references
HttpRequest.Reset();
SupabaseClient = nullptr;
Connection = nullptr;
WorldContextObject = nullptr;
QueryFilter = nullptr;
}
UPROPERTY() for garbage collectionSetReadyToDestroy() when operation completeshttps://your-project.supabase.co/rest/v1/table_name?select=columns&filter_conditions
# Simple query
/rest/v1/users?select=*
# Filtered query
/rest/v1/users?select=id,name,email&status=eq.active&age=gt.18&limit=10&order=created_at.desc
# Complex query with relationships
/rest/v1/posts?select=*,author:users(name,email)&published=eq.true
// Query actors by type
UQueryFilter* Filter = USupabaseUtils::MakeQueryFilter();
Filter->AddFilter(TEXT("actor_class"), EFilterOperator::Equals, TEXT("APlayerCharacter"));
Filter->AddFilter(TEXT("level_name"), EFilterOperator::Equals, GetWorld()->GetMapName());
UAsyncQuery* Query = UAsyncQuery::QueryTableAsync(
this,
TEXT("persistent_actors"),
TEXT("*"),
Filter
);
// Query current state, then subscribe to changes
void AMultiplayerGameState::InitializeGameData()
{
// First, get current game state
UAsyncQuery* InitialQuery = UAsyncQuery::QueryTableAsync(
this, TEXT("game_sessions"), TEXT("*")
);
InitialQuery->OnSuccess.AddDynamic(this, &AMultiplayerGameState::OnInitialDataLoaded);
// Then subscribe to real-time updates
UAsyncRealtime* RealtimeNode = UAsyncRealtime::SubscribeToTableAsync(
this, TEXT("game_sessions")
);
RealtimeNode->OnInsert.AddDynamic(this, &AMultiplayerGameState::OnPlayerJoined);
RealtimeNode->OnUpdate.AddDynamic(this, &AMultiplayerGameState::OnGameStateChanged);
}
Query Returns Empty Results:
Performance Issues:
SELECT *Memory Leaks:
// Enable detailed logging
USupabaseManager::EnableDebugLogging(this, true);
// Check connection status
FString Status = USupabaseManager::GetConnectionStatus(this);
UE_LOG(LogTemp, Warning, TEXT("Connection Status: %s"), *Status);