The Supabase UE5 Plugin implements a production-ready, thread-safe architecture designed for enterprise-grade game development. The system uses a centralized subsystem pattern with comprehensive memory management, connection pooling, and robust error handling.
The plugin is built around Unreal Engine’s Game Instance Subsystem pattern, providing:
// Main entry point - automatically initialized by UE5
USupabaseSubsystem* Subsystem = GetGameInstance()->GetSubsystem<USupabaseSubsystem>();
Primary data asset containing all connection configuration:
UPROPERTY(BlueprintReadWrite, EditAnywhere)
FName ConnectionName; // Identifier for multiple connections
UPROPERTY(BlueprintReadWrite, EditAnywhere)
FString SupabaseServerUrl; // https://your-project.supabase.co
UPROPERTY(BlueprintReadWrite, EditAnywhere)
FSupabaseCredentials SupabaseCredentials; // Keys and tokens
Core HTTP client responsible for all Supabase API interactions:
// Centralized client instance managed by subsystem
USupabaseClient* GetSupabaseClient() const { return SupabaseClient; }
// All operations reuse the single client instance
// No manual client creation required
All operations follow a consistent async pattern with dual-mode support:
// Standard mode - uses subsystem (recommended)
static UAsyncInsertRow* InsertRowAsync(
UObject* WorldContext,
const FString& Table,
const FJsonObjectWrapper& RowData
);
// Advanced mode - direct connection for edge cases
static UAsyncInsertRow* InsertRowAdvanced(
UObject* WorldContext,
USupabaseConnection* Connection,
const FString& Table,
const FJsonObjectWrapper& RowData
);
Every async node implements:
BeginDestroy() overridevirtual void BeginDestroy() override
{
SafeCleanup();
Super::BeginDestroy();
}
void SafeCleanup()
{
if (HttpRequest.IsValid())
{
HttpRequest->CancelRequest();
HttpRequest.Reset();
}
UnbindDelegates();
}
Centralized authentication state with automatic token refresh:
struct FSupabaseCredentials
{
FString AnonymousKey; // Public operations
FString ServiceKey; // Admin operations
FString UserAuthenticatedKey; // User operations
FString RefreshToken; // Token refresh
};
Thread-safe realtime connections with:
// Realtime event delegates
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(
FSupabaseRealtimeEventDelegate,
const FString&, EventType,
const FString&, Payload
);
Production-ready structs for all Supabase data types:
USTRUCT(BlueprintType)
struct FUser
{
UPROPERTY(BlueprintReadWrite, EditAnywhere)
FString Id;
UPROPERTY(BlueprintReadWrite, EditAnywhere)
FString Email;
UPROPERTY(BlueprintReadWrite, EditAnywhere)
FAppMetadata AppMetadata;
UPROPERTY(BlueprintReadWrite, EditAnywhere)
TArray<FIdentity> Identities;
// Full authentication details...
};
Built-in support for game object persistence:
USTRUCT(BlueprintType)
struct FActorPersistenceData
{
FString ActorId;
FTransform ActorTransform;
FString LevelName;
FString CustomData;
};
Static utility class providing simplified Blueprint access:
// Easy subsystem access
UFUNCTION(BlueprintPure, meta = (WorldContext = "WorldContext"))
static USupabaseSubsystem* GetSupabaseSubsystem(const UObject* WorldContext);
// Quick status checks
UFUNCTION(BlueprintPure, meta = (WorldContext = "WorldContext"))
static bool IsConnected(const UObject* WorldContext);
UFUNCTION(BlueprintPure, meta = (WorldContext = "WorldContext"))
static bool IsAuthenticated(const UObject* WorldContext);
// Common operations
UFUNCTION(BlueprintCallable, meta = (WorldContext = "WorldContext"))
static void LoginWithEmail(const UObject* WorldContext, const FString& Email, const FString& Password);
Comprehensive validation and error recovery:
bool ValidateInputs()
{
if (Table.IsEmpty())
{
BroadcastError("Table name cannot be empty");
return false;
}
// Security validation
if (ContainsSQLInjection(Table))
{
BroadcastError("Invalid characters in table name");
return false;
}
return true;
}
All realtime operations use proper synchronization:
// Critical sections for shared state
FCriticalSection RealtimeMutex;
void UpdateRealtimeState()
{
FScopeLock Lock(&RealtimeMutex);
// Thread-safe state modifications
}
void AMyGameInstance::BeginPlay()
{
Super::BeginPlay();
// Get subsystem
USupabaseSubsystem* Supabase = GetSubsystem<USupabaseSubsystem>();
// Create connection
USupabaseConnection* Connection = NewObject<USupabaseConnection>();
Connection->SupabaseServerUrl = TEXT("https://your-project.supabase.co");
Connection->SupabaseCredentials.AnonymousKey = TEXT("your-anon-key");
// Initialize
Supabase->InitializeConnection(Connection);
}
// Simple usage
Get Supabase Manager -> Is Connected
Get Supabase Manager -> Login With Email
// Advanced usage
Get Game Instance -> Get Subsystem (Supabase Subsystem) -> Get Supabase Client
// Insert data
UAsyncInsertRow* InsertTask = UAsyncInsertRow::InsertRowAsync(
this,
TEXT("players"),
PlayerDataJson
);
InsertTask->OnSuccess.AddDynamic(this, &AMyActor::OnInsertSuccess);
InsertTask->OnError.AddDynamic(this, &AMyActor::OnInsertError);
LogSupabase VerboseThis architecture provides enterprise-grade reliability while maintaining ease of use for both Blueprint and C++ developers. The centralized subsystem pattern ensures consistent behavior across all game systems while the async operation pattern provides non-blocking, memory-safe database interactions.