Version 1.34.2 | Production Ready | Unreal Engine 5.5+
This guide covers all database insert operations available in the Supabase UE5 Plugin, including synchronous client methods, asynchronous Blueprint nodes, and production-ready patterns.
The Supabase UE5 Plugin provides multiple ways to insert data into your database:
// Create JSON data
USupabaseJsonObjectWrapper* JsonData = USupabaseUtils::MakeSupabaseJson();
JsonData->SetStringField(TEXT("name"), TEXT("John Doe"));
JsonData->SetStringField(TEXT("email"), TEXT("john@example.com"));
JsonData->SetNumberField(TEXT("age"), 30);
// Insert using Manager (simplest approach)
UAsyncInsertRow* InsertTask = UAsyncInsertRow::InsertRowAsync(
this,
TEXT("users"),
*JsonData,
ESupabaseKey::Service
);
InsertTask->OnSuccess.AddDynamic(this, &AMyClass::OnInsertSuccess);
InsertTask->OnFailure.AddDynamic(this, &AMyClass::OnInsertFailure);
The plugin supports three authentication types for insert operations:
| Authentication Type | Use Case | Required Permission |
|---|---|---|
| Anonymous | Public inserts | No authentication required |
| Authenticated | User-specific inserts | Valid user session |
| Service | Admin/system inserts | Service role key |
// Anonymous insert (public tables)
UAsyncInsertRow::InsertRowAsync(this, TEXT("public_table"), JsonData, ESupabaseKey::Anonymous);
// Authenticated insert (user must be logged in)
UAsyncInsertRow::InsertRowAsync(this, TEXT("user_posts"), JsonData, ESupabaseKey::Authenticated);
// Service insert (admin operations)
UAsyncInsertRow::InsertRowAsync(this, TEXT("admin_logs"), JsonData, ESupabaseKey::Service);
Direct insert operation using the Supabase client:
// Get client from subsystem
USupabaseSubsystem* Subsystem = USupabaseManager::GetSupabaseSubsystem(this);
USupabaseClient* Client = Subsystem->GetSupabaseClient();
// Create JSON data
FJsonObjectWrapper RowData;
TSharedPtr<FJsonObject> JsonObject = MakeShareable(new FJsonObject);
JsonObject->SetStringField(TEXT("name"), TEXT("Jane Smith"));
JsonObject->SetStringField(TEXT("email"), TEXT("jane@example.com"));
RowData.JsonObject = JsonObject;
// Execute insert
Client->InsertRow(TEXT("users"), RowData, EAuthenticationToken::Service);
// Bind to client events
Client->SupabaseRequestSuccessful.AddDynamic(this, &AMyClass::OnInsertSuccess);
Client->SupabaseRequestFailed.AddDynamic(this, &AMyClass::OnInsertFailure);
UFUNCTION()
void AMyClass::OnInsertSuccess(FString Response, const FJsonObjectWrapper& JsonResponse)
{
UE_LOG(LogTemp, Log, TEXT("Insert successful: %s"), *Response);
}
UFUNCTION()
void AMyClass::OnInsertFailure(FString Error)
{
UE_LOG(LogTemp, Error, TEXT("Insert failed: %s"), *Error);
}
The primary async insert class with full production features:
UAsyncInsertRow* UAsyncInsertRow::InsertRowAsync(
UObject* WorldContextObject,
const FString& Table,
const FJsonObjectWrapper& RowData,
ESupabaseKey KeyType = ESupabaseKey::Service
);
UAsyncInsertRow* UAsyncInsertRow::InsertRowAdvanced(
UObject* WorldContextObject,
USupabaseConnection* Connection,
const FString& Table,
const FJsonObjectWrapper& RowData,
ESupabaseKey KeyType = ESupabaseKey::Service
);
void AMyGameMode::InsertPlayerData()
{
// Create player data
USupabaseJsonObjectWrapper* PlayerData = USupabaseUtils::MakeSupabaseJson();
PlayerData->SetStringField(TEXT("player_name"), TEXT("PlayerOne"));
PlayerData->SetNumberField(TEXT("score"), 1500);
PlayerData->SetNumberField(TEXT("level"), 42);
PlayerData->SetBoolField(TEXT("is_premium"), true);
// Execute async insert
UAsyncInsertRow* InsertTask = UAsyncInsertRow::InsertRowAsync(
this,
TEXT("player_stats"),
*PlayerData,
ESupabaseKey::Authenticated
);
// Bind events
InsertTask->OnSuccess.AddDynamic(this, &AMyGameMode::OnPlayerInsertSuccess);
InsertTask->OnFailure.AddDynamic(this, &AMyGameMode::OnPlayerInsertFailure);
}
UFUNCTION()
void AMyGameMode::OnPlayerInsertSuccess()
{
UE_LOG(LogTemp, Log, TEXT("Player data inserted successfully"));
// Handle success (update UI, etc.)
}
UFUNCTION()
void AMyGameMode::OnPlayerInsertFailure(FString Error)
{
UE_LOG(LogTemp, Error, TEXT("Failed to insert player data: %s"), *Error);
// Handle error (show error message, retry, etc.)
}
// Create JSON wrapper
USupabaseJsonObjectWrapper* JsonData = USupabaseUtils::MakeSupabaseJson();
// Add different data types
JsonData->SetStringField(TEXT("name"), TEXT("Product Name"));
JsonData->SetNumberField(TEXT("price"), 29.99);
JsonData->SetBoolField(TEXT("in_stock"), true);
JsonData->SetStringField(TEXT("category"), TEXT("electronics"));
// Add arrays
TArray<FString> Tags = {TEXT("popular"), TEXT("featured"), TEXT("new")};
JsonData->SetStringArrayField(TEXT("tags"), Tags);
// Add nested objects
USupabaseJsonObjectWrapper* MetaData = USupabaseUtils::MakeSupabaseJson();
MetaData->SetStringField(TEXT("created_by"), TEXT("admin"));
MetaData->SetStringField(TEXT("source"), TEXT("import"));
JsonData->SetObjectField(TEXT("metadata"), *MetaData);
// Create JSON object manually
FJsonObjectWrapper RowData;
TSharedPtr<FJsonObject> JsonObject = MakeShareable(new FJsonObject);
// Add fields
JsonObject->SetStringField(TEXT("title"), TEXT("My Blog Post"));
JsonObject->SetStringField(TEXT("content"), TEXT("This is the blog content..."));
JsonObject->SetBoolField(TEXT("published"), false);
// Add timestamp
FDateTime Now = FDateTime::UtcNow();
JsonObject->SetStringField(TEXT("created_at"), Now.ToIso8601());
// Create array
TArray<TSharedPtr<FJsonValue>> TagsArray;
TagsArray.Add(MakeShareable(new FJsonValueString(TEXT("unreal"))));
TagsArray.Add(MakeShareable(new FJsonValueString(TEXT("gamedev"))));
JsonObject->SetArrayField(TEXT("tags"), TagsArray);
RowData.JsonObject = JsonObject;
The plugin performs comprehensive validation:
bool UAsyncInsertRow::ValidateInputs()
{
// World context validation
if (!IsValid(WorldContextObject))
{
OnFailure.Broadcast(TEXT("Invalid world context"));
return false;
}
// Table name validation
if (Table.IsEmpty())
{
OnFailure.Broadcast(TEXT("Table name cannot be empty"));
return false;
}
// Table name format validation
for (TCHAR Character : Table)
{
if (!FChar::IsAlnum(Character) && Character != '_')
{
OnFailure.Broadcast(TEXT("Invalid table name format"));
return false;
}
}
// JSON data validation
if (!RowData.JsonObject.IsValid())
{
OnFailure.Broadcast(TEXT("Invalid JSON data"));
return false;
}
return true;
}
| Error Type | Cause | Solution |
|---|---|---|
| Authentication Failed | Invalid or expired key | Check key validity and user session |
| Permission Denied | Insufficient privileges | Use appropriate authentication type |
| Invalid JSON | Malformed data structure | Validate JSON structure before insert |
| Table Not Found | Non-existent table name | Verify table exists in database |
| Connection Failed | Network or server issues | Implement retry logic |
UFUNCTION()
void AMyClass::OnInsertFailure(FString Error)
{
// Log the error
UE_LOG(LogTemp, Error, TEXT("Insert operation failed: %s"), *Error);
// Parse error type and handle accordingly
if (Error.Contains(TEXT("authentication")))
{
// Handle authentication errors
HandleAuthenticationError();
}
else if (Error.Contains(TEXT("permission")))
{
// Handle permission errors
HandlePermissionError();
}
else if (Error.Contains(TEXT("network")))
{
// Handle network errors - maybe retry
RetryInsertOperation();
}
else
{
// Handle generic errors
ShowGenericErrorMessage(Error);
}
}
The plugin provides Blueprint-friendly async nodes:
// This creates a Blueprint-callable async node
UFUNCTION(BlueprintCallable, meta = (BlueprintInternalUseOnly = "true", WorldContext = "WorldContextObject"),
Category = "Supabase|Async")
static UAsyncInsertRow* InsertRowAsync(
UObject* WorldContextObject,
const FString& Table,
const FJsonObjectWrapper& RowData,
ESupabaseKey KeyType = ESupabaseKey::Service
);
void UAsyncInsertRow::BeginDestroy()
{
SafeCleanup();
Super::BeginDestroy();
}
void UAsyncInsertRow::SafeCleanup()
{
// Cancel pending HTTP requests
CancelPendingRequest();
// Unbind events
UnbindEvents();
// Clear references
SupabaseClient = nullptr;
Connection = nullptr;
// Mark for destruction
SetReadyToDestroy();
}
// All operations are thread-safe and execute on the game thread
void UAsyncInsertRow::HandleInsertSuccess(FString Response, const FJsonObjectWrapper& JsonResponse)
{
// Ensure we're on the game thread
if (!IsInGameThread())
{
AsyncTask(ENamedThreads::GameThread, [this, Response, JsonResponse]()
{
HandleInsertSuccess(Response, JsonResponse);
});
return;
}
// Safe to broadcast events on game thread
OnSuccess.Broadcast();
SetReadyToDestroy();
}
bool UAsyncInsertRow::SetupClient()
{
if (bUseSubsystem)
{
// Use subsystem for automatic connection management
USupabaseSubsystem* Subsystem = USupabaseManager::GetSupabaseSubsystem(WorldContextObject);
if (!Subsystem)
{
OnFailure.Broadcast(TEXT("Supabase subsystem not available"));
return false;
}
Connection = Subsystem->GetActiveConnection();
SupabaseClient = Subsystem->GetSupabaseClient();
}
else
{
// Use direct connection
SupabaseClient = NewObject<USupabaseClient>(this);
SupabaseClient->SetSupabaseConnection(Connection);
}
return IsValid(SupabaseClient) && IsValid(Connection);
}
For multiple record inserts, use array format:
// Create array of objects
TArray<TSharedPtr<FJsonValue>> RecordsArray;
// Add multiple records
for (int32 i = 0; i < PlayerCount; i++)
{
TSharedPtr<FJsonObject> PlayerRecord = MakeShareable(new FJsonObject);
PlayerRecord->SetStringField(TEXT("name"), FString::Printf(TEXT("Player%d"), i));
PlayerRecord->SetNumberField(TEXT("score"), FMath::RandRange(100, 1000));
RecordsArray.Add(MakeShareable(new FJsonValueObject(PlayerRecord)));
}
// Create wrapper for array
FJsonObjectWrapper BatchData;
TSharedPtr<FJsonObject> JsonObject = MakeShareable(new FJsonObject);
JsonObject->SetArrayField(TEXT("records"), RecordsArray);
BatchData.JsonObject = JsonObject;
Use PostgreSQL’s ON CONFLICT clause:
// Add conflict resolution to your insert
JsonObject->SetStringField(TEXT("email"), TEXT("user@example.com"));
JsonObject->SetStringField(TEXT("name"), TEXT("Updated Name"));
// The database will handle conflicts based on your table constraints
UAsyncInsertRow::InsertRowAsync(this, TEXT("users"), BatchData, ESupabaseKey::Service);
UFUNCTION()
void AMyClass::OnInsertSuccess()
{
// Insert successful - the new record is now in the database
// The response contains the inserted record with any server-generated fields
// Update UI
UpdatePlayerList();
// Trigger events
OnPlayerAdded.Broadcast();
// Log success
UE_LOG(LogTemp, Log, TEXT("Player record inserted successfully"));
}
// Add performance logging
void UAsyncInsertRow::ExecuteInsert()
{
StartTime = FDateTime::UtcNow();
// ... insert logic ...
UE_LOG(LogTemp, Verbose, TEXT("Insert request sent for table: %s"), *Table);
}
void UAsyncInsertRow::HandleInsertSuccess(FString Response, const FJsonObjectWrapper& JsonResponse)
{
FTimespan Duration = FDateTime::UtcNow() - StartTime;
UE_LOG(LogTemp, Log, TEXT("Insert completed in %f seconds"), Duration.GetTotalSeconds());
OnSuccess.Broadcast();
}
// Check if user is properly authenticated
USupabaseSubsystem* Subsystem = USupabaseManager::GetSupabaseSubsystem(this);
if (!Subsystem->IsAuthenticated())
{
// User needs to log in first
USupabaseManager::LoginWithEmail(this, UserEmail, UserPassword);
}
// Validate JSON before insert
if (!RowData.JsonObject.IsValid())
{
UE_LOG(LogTemp, Error, TEXT("JSON object is not valid"));
return;
}
// Check for required fields
if (!RowData.JsonObject->HasField(TEXT("required_field")))
{
UE_LOG(LogTemp, Error, TEXT("Missing required field"));
return;
}
// Implement retry logic
void AMyClass::RetryInsertWithBackoff()
{
RetryCount++;
if (RetryCount <= MaxRetries)
{
float DelaySeconds = FMath::Pow(2.0f, RetryCount); // Exponential backoff
GetWorld()->GetTimerManager().SetTimer(
RetryTimerHandle,
this,
&AMyClass::ExecuteInsertRetry,
DelaySeconds,
false
);
}
}
| Method | Description | Parameters |
|---|---|---|
InsertRowAsync |
Basic async insert using subsystem | WorldContext, Table, RowData, KeyType |
InsertRowAdvanced |
Advanced async insert with connection | WorldContext, Connection, Table, RowData, KeyType |
| Event | Description | Parameters |
|---|---|---|
OnSuccess |
Fired on successful insert | None |
OnFailure |
Fired on insert failure | Error (FString) |
| Value | Description |
|---|---|
Anonymous |
Public access using anonymous key |
Authenticated |
User access requiring valid session |
Service |
Admin access using service role key |
For more information, see the Configuration Guide and Examples.