Version 1.34.2 | Production Ready | Unreal Engine 5.5+
The Supabase UE5 Plugin provides comprehensive update operations for modifying existing data in your Supabase database. All update operations are thread-safe, memory-managed, and Blueprint-friendly with robust error handling.
Update operations in the Supabase plugin use the HTTP PATCH method to modify existing rows in your database tables. The plugin provides multiple approaches to update data:
The UAsyncUpdateRow class is the recommended approach for production applications, providing advanced features and subsystem integration.
UCLASS()
class SUPABASE_API UAsyncUpdateRow : public UBlueprintAsyncActionBase
{
GENERATED_BODY()
public:
// Blueprint Events
UPROPERTY(BlueprintAssignable)
FSupabaseBooleanDelegate OnSuccess;
UPROPERTY(BlueprintAssignable)
FSupabaseErrorEvent OnFailure;
// Static Factory Methods
static UAsyncUpdateRow* UpdateRowAsync(/* parameters */);
static UAsyncUpdateRow* UpdateRowAdvanced(/* parameters */);
static UAsyncUpdateRow* UpdateRowWithFilterAsync(/* parameters */);
static UAsyncUpdateRow* UpdateRowWithQueryAsync(/* parameters */);
};
Basic update operation using subsystem integration:
UAsyncUpdateRow* UpdateTask = UAsyncUpdateRow::UpdateRowAsync(
this, // WorldContextObject
nullptr, // Connection (uses subsystem)
TEXT("users"), // Table name
TEXT("123"), // Row ID
UpdatedData, // JSON data wrapper
ESupabaseKey::Service // Authentication type
);
UpdateTask->OnSuccess.AddDynamic(this, &AMyActor::OnUpdateSuccess);
UpdateTask->OnFailure.AddDynamic(this, &AMyActor::OnUpdateFailed);
Full control over connection and configuration:
UAsyncUpdateRow* UpdateTask = UAsyncUpdateRow::UpdateRowAdvanced(
this, // WorldContextObject
MyConnection, // Specific connection object
TEXT("profiles"), // Table name
TEXT("user-uuid-456"), // Row ID
ProfileData, // Updated profile data
ESupabaseKey::Authenticated, // Use user token
30.0f // Request timeout
);
Update based on custom field filtering instead of ID:
// Update user by email address
UAsyncUpdateRow* UpdateTask = UAsyncUpdateRow::UpdateRowWithFilterAsync(
this, // WorldContextObject
TEXT("users"), // Table name
TEXT("email"), // Filter field
TEXT("user@example.com"), // Filter value
UpdatedData, // New data
ESupabaseKey::Service // Authentication
);
Complex updates using PostgREST query syntax:
// Update all users with status 'pending' to 'active'
FString QueryFilter = TEXT("status=eq.pending&created_at=gte.2024-01-01");
UAsyncUpdateRow* UpdateTask = UAsyncUpdateRow::UpdateRowWithQueryAsync(
this, // WorldContextObject
TEXT("users"), // Table name
QueryFilter, // PostgREST query
StatusUpdate, // Updated data
ESupabaseKey::Service // Authentication
);
The Update Row Async Blueprint node provides simple update functionality:
Inputs:
Outputs:
// Prepare update data
TSharedPtr<FJsonObject> JsonObject = MakeShareable(new FJsonObject());
JsonObject->SetStringField(TEXT("name"), TEXT("Updated Name"));
JsonObject->SetStringField(TEXT("email"), TEXT("newemail@example.com"));
JsonObject->SetStringField(TEXT("updated_at"), FDateTime::Now().ToString());
FJsonObjectWrapper UpdateWrapper;
UpdateWrapper.JsonObject = JsonObject;
// Execute update
UAsyncUpdateRow* UpdateTask = UAsyncUpdateRow::UpdateRowAsync(
this, nullptr, TEXT("users"), TEXT("123"), UpdateWrapper, ESupabaseKey::Service
);
In Blueprint, use the Make JsonObjectWrapper node to construct your update data:
enum class ESupabaseKey : uint8
{
Service, // Service role key (full access)
Authenticated, // User session token (RLS applies)
Anonymous // Anonymous key (limited access)
};
Authentication Errors:
void AMyActor::OnUpdateFailed(const FString& ErrorMessage)
{
if (ErrorMessage.Contains(TEXT("JWT")))
{
// Token expired or invalid
RefreshUserToken();
}
else if (ErrorMessage.Contains(TEXT("permission denied")))
{
// RLS policy violation
HandlePermissionError();
}
}
Data Validation Errors:
void AMyActor::OnUpdateFailed(const FString& ErrorMessage)
{
if (ErrorMessage.Contains(TEXT("violates check constraint")))
{
// Data validation failed
ShowValidationErrorToUser(ErrorMessage);
}
else if (ErrorMessage.Contains(TEXT("foreign key constraint")))
{
// Referenced record doesn't exist
HandleReferenceError();
}
}
| Error Type | Description | Typical Causes |
|---|---|---|
| HTTP 400 | Bad Request | Malformed JSON, invalid column names |
| HTTP 401 | Unauthorized | Missing or invalid authentication |
| HTTP 403 | Forbidden | RLS policy denial, insufficient permissions |
| HTTP 404 | Not Found | Table or row doesn’t exist |
| HTTP 409 | Conflict | Unique constraint violation |
| HTTP 422 | Unprocessable Entity | Data validation failure |
// ✅ GOOD: Uses subsystem (connection reuse)
UAsyncUpdateRow* UpdateTask = UAsyncUpdateRow::UpdateRowAsync(
this, nullptr, TEXT("users"), RowId, Data, ESupabaseKey::Service
);
// ❌ AVOID: Creates new connection each time
UAsyncUpdateRow* UpdateTask = UAsyncUpdateRow::UpdateRowAdvanced(
this, NewConnection, TEXT("users"), RowId, Data, ESupabaseKey::Service
);
For multiple updates, consider using database transactions or batch operations:
// Update multiple related records
void AMyActor::UpdateUserProfile(const FString& UserId)
{
// Update user record
UpdateUserRecord(UserId);
// Update profile record
UpdateProfileRecord(UserId);
// Update preferences
UpdateUserPreferences(UserId);
}
The AsyncUpdateRow class automatically handles cleanup:
void UAsyncUpdateRow::SafeCleanup()
{
if (HttpRequest.IsValid())
{
HttpRequest->OnProcessRequestComplete().Unbind();
HttpRequest->CancelRequest();
HttpRequest.Reset();
}
// Clear references
SupabaseClient = nullptr;
Connection = nullptr;
}
The plugin integrates with the Entity Persistence system for automatic data saving:
void UEntityPersistence::OnRowExistResult(FString Response, bool bExists)
{
if (bExists)
{
// Update existing entity
UAsyncUpdateRow* UpdateTask = UAsyncUpdateRow::UpdateRowWithFilterAsync(
this,
GetTableName(),
TEXT("actor_id"),
EntityId,
JsonWrapper,
ESupabaseKey::Service
);
UpdateTask->OnSuccess.AddDynamic(this, &UEntityPersistence::OnSaveSuccess);
UpdateTask->OnFailure.AddDynamic(this, &UEntityPersistence::OnSaveFailed);
}
}
Update operations work seamlessly with real-time subscriptions:
// Set up real-time listener
UAsyncRealtime* RealtimeTask = UAsyncRealtime::RealtimeAsync(
this, TEXT("users"), TEXT("UPDATE")
);
RealtimeTask->OnUpdate.AddDynamic(this, &AMyActor::OnUserUpdated);
// Perform update (will trigger real-time event)
UAsyncUpdateRow* UpdateTask = UAsyncUpdateRow::UpdateRowAsync(
this, nullptr, TEXT("users"), UserId, UpdatedData, ESupabaseKey::Service
);
bool ValidateUpdateData(const FJsonObjectWrapper& Data)
{
if (!Data.JsonObject.IsValid())
{
return false;
}
// Check required fields
if (!Data.JsonObject->HasField(TEXT("updated_at")))
{
Data.JsonObject->SetStringField(TEXT("updated_at"), FDateTime::Now().ToString());
}
return true;
}
void AMyActor::OptimisticUpdate(const FString& UserId, const FString& NewName)
{
// Update UI immediately
UpdateUIWithNewName(NewName);
// Send update to server
UAsyncUpdateRow* UpdateTask = UAsyncUpdateRow::UpdateRowAsync(
this, nullptr, TEXT("users"), UserId, Data, ESupabaseKey::Authenticated
);
UpdateTask->OnFailure.AddDynamic(this, &AMyActor::OnUpdateFailed);
}
void AMyActor::OnUpdateFailed(const FString& ErrorMessage)
{
// Revert UI changes
RevertUIChanges();
// Show error to user
ShowErrorMessage(ErrorMessage);
}
When using authenticated keys, ensure your RLS policies allow updates:
-- Example RLS policy for user updates
CREATE POLICY "Users can update own record" ON users
FOR UPDATE USING (auth.uid() = id);
Update Not Applied:
Permission Denied:
Data Validation Failed:
Enable detailed logging for troubleshooting:
// Enable in DefaultEngine.ini
[Core.Log]
LogSupabase=VeryVerbose
LogTemp=VeryVerbose
void AMyActor::TestUpdateOperation()
{
UE_LOG(LogTemp, Warning, TEXT("Testing update operation..."));
UAsyncUpdateRow* UpdateTask = UAsyncUpdateRow::UpdateRowAsync(
this, nullptr, TEXT("test_table"), TEXT("1"), TestData, ESupabaseKey::Service
);
UpdateTask->OnSuccess.AddDynamic(this, &AMyActor::OnUpdateSuccess);
UpdateTask->OnFailure.AddDynamic(this, &AMyActor::OnUpdateFailed);
}
// ❌ OLD: Direct client usage
SupabaseClient->UpdateRow(TEXT("users"), TEXT("123"), Data, EAuthenticationToken::Service);
// ✅ NEW: Async with subsystem
UAsyncUpdateRow* UpdateTask = UAsyncUpdateRow::UpdateRowAsync(
this, nullptr, TEXT("users"), TEXT("123"), Data, ESupabaseKey::Service
);
// ❌ OLD: Blocking function library call
USupabaseFunctionLibrary::UpdateRow(/* parameters */);
// ✅ NEW: Non-blocking async operation
UAsyncUpdateRow* UpdateTask = UAsyncUpdateRow::UpdateRowAsync(/* parameters */);
Next: Delete Operations
Previous: Insert Operations