B4A Library [B4X] Supabase - The Open Source Firebase alternative

1693165470237.png

Supabase is an open source Firebase alternative. It provides all the backend services you need to build a product. Supabase uses Postgres database with real-time capabilities. Basically, supabase provides an interface to manage postgres database that you can use to create table and insert, edit and delete data in the table.

We can use REST API or client libraries from supabase to access the data in the postgres database. Supabase is not just about accessing the database. it also provides some solutions out of the box such as Authentication, File Storage and Real-time capabilities.

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------

I am a huge fan of this project and wanted to develop a client library for B4X 2 years ago. With supabase you can develop an online app without having your own backend, no VPS or other server is needed. No matter how many users the app has, the API can scale.

A big goal of the client library for b4x is to be listed on the website among the programming languages that have a community client library. But until then there is still a lot to do.

Feature
Components
Status
AuthenticationE-Mail +Password, SignIn with oauth (Google and Apple)Alpha test
DatabaseCreate, Read, Update, Delete, RPCBeta test
StorageCRUD Buckets and CRUD FilesBeta test
RealtimePostgresChanges, Broadcast and PresenceAlpha test

Roadmap
Examples and Tutorials
Supabase
Author: Alexander Stolte
Version: 1.10

  • Supabase
    • Events:
      • AuthStateChange (StateType As String)
      • RangeDownloadTracker (Tracker As SupabaseRangeDownloadTracker)
    • Functions:
      • Class_Globals As String
      • getApiKey As String
      • getAuth As Supabase_Authentication
      • getDatabase As Supabase_Database
      • getStorage As Supabase_Storage
      • getURL As String
      • Initialize (URL As String, AnonKey As String) As String
        Initializes the object. You can add parameters to this method if needed.
      • InitializeEvents (Callback As Object, EventName As String) As String
      • IsInitialized As Boolean
        Tests whether the object has been initialized.
    • Properties:
      • ApiKey As String [read only]
      • Auth As Supabase_Authentication [read only]
      • Database As Supabase_Database [read only]
      • Storage As Supabase_Storage [read only]
      • URL As String [read only]
  • SupabaseDatabaseResult
    • Fields:
      • Columns As Map
      • Error As SupabaseError
      • IsInitialized As Boolean
        Tests whether the object has been initialized.
      • Rows As List
      • Tag As Object
    • Functions:
      • Initialize
        Initializes the fields to their default value.
  • SupabaseError
    • Fields:
      • ErrorMessage As String
      • IsInitialized As Boolean
        Tests whether the object has been initialized.
      • StatusCode As Int
      • Success As Boolean
    • Functions:
      • Initialize
        Initializes the fields to their default value.
  • SupabaseRangeDownloadTracker
    • Fields:
      • Cancel As Boolean
      • Completed As Boolean
      • CurrentLength As Long
      • IsInitialized As Boolean
        Tests whether the object has been initialized.
      • TotalLength As Long
    • Functions:
      • Initialize
        Initializes the fields to their default value.
  • SupabaseRealtime
    • Events:
      • Connected
      • DataReceived (Data As SupabaseRealtime_Data)
      • Subscribed
    • Fields:
      • Event_ALL As String
      • Event_DELETE As String
      • Event_INSERT As String
      • Event_UPDATE As String
    • Functions:
      • Channel (SchemaName As String, TableName As String, EventName As String) As SupabaseRealtime_Channel
        SchemaName - public
        Available Events:
        "*" | "INSERT" | "UPDATE" | "DELETE"
        Default: *
      • Close As String
      • Connect As String
      • Initialize (Callback As Object, EventName As String, ThisSupabase As Supabase) As String
        Initializes the object. You can add parameters to this method if needed.
      • IsInitialized As Boolean
        Tests whether the object has been initialized.
      • RemoveChannel (ThisChannel As SupabaseRealtime_Channel) As String
  • SupabaseRealtime_Channel
    • Functions:
      • Class_Globals As String
      • Close As String
      • getTopic As String
      • Initialize (Client As SupabaseRealtime_Client, Topic As String, SchemaName As String, TableName As String, EventName As String, ThisSupabase As Supabase) As String
        Initializes the object. You can add parameters to this method if needed.
      • IsInitialized As Boolean
        Tests whether the object has been initialized.
      • Subscribe As SupabaseRealtime_Channel
      • Unsubscribe As SupabaseRealtime_Channel
    • Properties:
      • Topic As String [read only]
  • SupabaseRealtime_Client
    • Functions:
      • Channel (SchemaName As String, TableName As String, EventName As String) As SupabaseRealtime_Channel
        SchemaName - public
        Available Events:
        "*" | "INSERT" | "UPDATE" | "DELETE"
        Default: *
      • Class_Globals As String
      • Close As String
      • Connect
      • Initialize (Callback As Object, EventName As String, ThisSupabase As Supabase, RealTime As SupabaseRealtime) As String
        Initializes the object. You can add parameters to this method if needed.
      • IsInitialized As Boolean
        Tests whether the object has been initialized.
      • RemoveChannel (ThisChannel As SupabaseRealtime_Channel) As String
      • SendMessage (jSonMessage As String) As String
  • SupabaseRealtime_Data
    • Fields:
      • Columns As List
      • CommitTimestamp As Long
      • DatabaseError As SupabaseError
      • EventType As String
      • IsInitialized As Boolean
        Tests whether the object has been initialized.
      • OldRecord As Map
      • Records As Map
      • Schema As String
      • Table As String
    • Functions:
      • Initialize
        Initializes the fields to their default value.
  • SupabaseStorageBucket
    • Fields:
      • AllowedMimeTypes As List
      • CreatedAt As Long
      • Error As SupabaseError
      • FileSizeLimit As Int
      • Id As String
      • IsInitialized As Boolean
        Tests whether the object has been initialized.
      • isPublic As Boolean
      • Name As String
      • Owner As String
      • UpdatedAt As Long
    • Functions:
      • Initialize
        Initializes the fields to their default value.
  • SupabaseStorageFile
    • Fields:
      • Error As SupabaseError
      • FileBody As Byte()
      • Id As String
      • IsInitialized As Boolean
        Tests whether the object has been initialized.
      • Key As String
      • PublicUrl As String
      • SignedURL As String
    • Functions:
      • Initialize
        Initializes the fields to their default value.
  • SupabaseStorageResult
    • Fields:
      • Error As SupabaseError
      • IsInitialized As Boolean
        Tests whether the object has been initialized.
    • Functions:
      • Initialize
        Initializes the fields to their default value.
  • SupabaseTokenInformations
    • Fields:
      • AccessExpiry As Long
      • AccessToken As String
      • Email As String
      • IsInitialized As Boolean
        Tests whether the object has been initialized.
      • RefreshToken As String
      • Tag As Object
      • TokenType As String
      • Valid As Boolean
    • Functions:
      • Initialize
        Initializes the fields to their default value.
  • SupabaseUser
    • Fields:
      • Aud As String
      • ConfirmationSentAt As Long
      • ConfirmedAt As Long
      • CreatedAt As Long
      • Email As String
      • EmailConfirmedAt As Long
      • Error As SupabaseError
      • Id As String
      • IsInitialized As Boolean
        Tests whether the object has been initialized.
      • json As JSONConverter
      • LastSignInAt As Long
      • Phone As String
      • Role As String
      • UpdatedAt As Long
    • Functions:
      • Initialize
        Initializes the fields to their default value.
  • Supabase_Authentication
    • Functions:
      • CallFromResume (Intent As Intent) As String
      • GetAccessToken As ResumableSub
      • getProvider_Apple As String
        B4I Only
      • getProvider_Google As String
      • GetUser As ResumableSub
        Gets the user object
        <code>Wait For (xSupabase.Auth.GetUser) Complete (User As SupabaseUser)</code>
      • Initialize (ThisSupabase As Supabase, EventName As String) As String
        Initializes the object. You can add parameters to this method if needed.
      • IsInitialized As Boolean
        Tests whether the object has been initialized.
      • isUserLoggedIn As ResumableSub
        Checks if the user is logged in, renews the access token if it has expired
        <code>Wait For (xSupabase.Auth.isUserLoggedIn) Complete (isLoggedIn As Boolean)</code>
      • Login_EmailPassword (Email As String, Password As String) As ResumableSub
        If an account is created, users can login to your app.
        code>
        Wait For (xSupabase.Auth.LogIn_EmailPassword("[email protected]","Test123!!")) Complete (User As SupabaseUser)
        If Result.Success Then
        Log("successfully logged in with " & User.Email)
        Else
        Log("Error: " & Result.ErrorMessage)
        End If
        </code>
      • LogIn_MagicLink (Email As String) As ResumableSub
        Send a user a passwordless link which they can use to redeem an access_token.
        code>
        Wait For (xSupabase.Auth.LogIn_MagicLink("[email protected]")) Complete (Result As SupabaseError)
        If Result.Success Then
        Log("magic link successfully sent")
        Else
        Log("Error: " & Result.ErrorMessage)
        End If
        </code>
      • Logout As ResumableSub
        User tokens are removed from the device
        After calling log out, all interactions using the Supabase B4X client will be "anonymous".
        <code>
        Wait For (xSupabase.Auth.Logout) Complete (Result As SupabaseError)
        If Result.Success Then
        Log("User successfully logged out")
        Else
        Log("Error: " & Result.ErrorMessage)
        End If
        </code>
      • PasswordRecovery (Email As String) As ResumableSub
        <code>
        wait for (xSupabase.Auth.PasswordRecovery("[email protected]")) Complete (Response As SupabaseError)
        If Response.Success Then
        Log("Recovery email sent successfully")
        Else
        Log("Error: " & Response.ErrorMessage)
        End If
        </code>
      • RefreshToken As ResumableSub
      • SaveToken As String
      • SignInWithOAuth (ClientId As String, Provider As String, Scope As String) As ResumableSub
        Signs the user in using third party OAuth providers.
        <code>
        #If B4A
        Wait For (xSupabase.Auth.SignInWithOAuth("xxx.apps.googleusercontent.com","google","profile email https://www.googleapis.com/auth/userinfo.email")) Complete (User As SupabaseUser)
        #Else If B4I
        Wait For (xSupabase.Auth.SignInWithOAuth("xxx.apps.googleusercontent.com","google","profile email https://www.googleapis.com/auth/userinfo.email")) Complete (User As SupabaseUser)
        #Else If B4J
        Wait For (xSupabase.Auth.SignInWithOAuth("xxx.apps.googleusercontent.com","google","profile email https://www.googleapis.com/auth/userinfo.email","xxx")) Complete (User As SupabaseUser)
        #End If
        If User.Error.Success Then
        Log("successfully logged in with " & User.Email)
        Else
        Log("Error: " & User.Error.ErrorMessage)
        End If
        </code>
      • SignUp (Email As String, Password As String) As ResumableSub
        Allow your users to sign up and create a new account.
        <code>
        wait for (xSupabase.Auth.SignUp("[email protected]","Test123!")) Complete (NewUser As SupabaseUser)
        If NewUser.Error.Success Then
        Log("successfully registered with " & NewUser.Email)
        Else
        Log("Error: " & NewUser.Error.ErrorMessage)
        End If
        </code>
      • TokenInformations As SupabaseTokenInformations
      • UpdateUser (NewEmail As String, NewPassword As String) As ResumableSub
        Update the user with a new email or password. Each key (email, password, and data) is optional
        If you don't want to change the password and only the email address, just leave the password blank
        If you don't want to change the email address and only the password, just leave the email blank
        <code>
        Wait For (xSupabase.Auth.UpdateUser("[email protected]","")) Complete (Result As SupabaseError)
        If Result.Success Then
        Log("User data successfully changed")
        Else
        Log("Error: " & Result.ErrorMessage)
        End If
        </code>
    • Properties:
      • Provider_Apple As String [read only]
        B4I Only
      • Provider_Google As String [read only]
  • Supabase_Database
    • Functions:
      • DeleteData As Supabase_DatabaseDelete
        <code>
        Dim Delete As Supabase_DatabaseDelete = xSupabase.Database.DeleteData
        Delete.From("dt_Tasks")
        Delete.Eq(CreateMap("Tasks_Id":15))
        Wait For (Delete.Execute) Complete (Result As SupabaseError)
        </code>
      • Initialize (ThisSupabase As Supabase) As String
        Initializes the object. You can add parameters to this method if needed.
      • InsertData As Supabase_DatabaseInsert
        One Row:
        <code>
        Dim Insert As Supabase_DatabaseInsert = xSupabase.Database.InsertData
        Insert.From("dt_Tasks")
        Dim InsertMap As Map = CreateMap("Tasks_Name":"Task 07","Tasks_Checked":False,"Tasks_CreatedAt":DateUtils.TicksToString(DateTime.Now),"Tasks_UpdatedAt":DateUtils.TicksToString(DateTime.Now))
        Wait For (Insert.Insert(InsertMap).Upsert.Execute) Complete (Result As SupabaseError)
        </code>
        Bulk Insert:
        <code>
        Dim Insert As Supabase_DatabaseInsert = xSupabase.Database.InsertData
        Insert.From("dt_Tasks")
        Dim lst_BulkInsert As List
        lst_BulkInsert.Initialize
        lst_BulkInsert.Add(CreateMap("Tasks_Name":"Task 05","Tasks_Checked":True,"Tasks_CreatedAt":DateUtils.TicksToString(DateTime.Now),"Tasks_UpdatedAt":DateUtils.TicksToString(DateTime.Now)))
        lst_BulkInsert.Add(CreateMap("Tasks_Name":"Task 06","Tasks_Checked":True,"Tasks_CreatedAt":DateUtils.TicksToString(DateTime.Now),"Tasks_UpdatedAt":DateUtils.TicksToString(DateTime.Now)))
        Wait For (Insert.InsertBulk(lst_BulkInsert).Execute) Complete (Result As SupabaseError)
        </code>
      • IsInitialized As Boolean
        Tests whether the object has been initialized.
      • PrintTable (Table As SupabaseDatabaseResult) As String
      • SelectData As Supabase_DatabaseSelect
        <code>
        Dim Query As Supabase_DatabaseSelect = xSupabase.Database.SelectData
        Query.Columns("*").From("dt_Tasks")
        Wait For (Query.Execute) Complete (DatabaseResult As SupabaseDatabaseResult)
        xSupabase.Database.PrintTable(DatabaseResult)
        </code>
      • UpdateData As Supabase_DatabaseUpdate
        <code>
        Dim Update As Supabase_DatabaseUpdate = xSupabase.Database.UpdateData
        Update.From("dt_Tasks")
        Update.Update(CreateMap("Tasks_Name":"Task 08"))
        Update.Eq(CreateMap("Tasks_Id":15))
        Wait For (Update.Execute) Complete (Result As SupabaseError)
        </code>
  • Supabase_DatabaseDelete
    • Functions:
      • Class_Globals As String
      • Eq (ColumnValue As Map) As Supabase_DatabaseDelete
      • Execute As ResumableSub
      • From (TableName As String) As Supabase_DatabaseDelete
      • Initialize (ThisSupabase As Supabase) As String
        Initializes the object. You can add parameters to this method if needed.
      • IsInitialized As Boolean
        Tests whether the object has been initialized.
  • Supabase_DatabaseInsert
    • Functions:
      • Class_Globals As String
      • Execute As ResumableSub
      • From (TableName As String) As Supabase_DatabaseInsert
      • Initialize (ThisSupabase As Supabase) As String
        Initializes the object. You can add parameters to this method if needed.
      • Insert (ColumnValue As Map) As Supabase_DatabaseInsert
        Insert one row
        <code>Dim InsertMap As Map = CreateMap("Tasks_Name":"Task 01","Tasks_Checked":True,"Tasks_CreatedAt":DateUtils.TicksToString(DateTime.Now))</code>
      • InsertBulk (ColumnValueList As List) As Supabase_DatabaseInsert
        Insert many rows
        <code> Dim lst_BulkInsert As List
        lst_BulkInsert.Initialize
        lst_BulkInsert.Add(CreateMap("Tasks_Name":"Task 01","Tasks_Checked":True,"Tasks_CreatedAt":DateUtils.TicksToString(DateTime.Now)))
        lst_BulkInsert.Add(CreateMap("Tasks_Name":"Task 02","Tasks_Checked":False,"Tasks_CreatedAt":DateUtils.TicksToString(DateTime.Now)))
        </code>
      • IsInitialized As Boolean
        Tests whether the object has been initialized.
      • Upsert As Supabase_DatabaseInsert
        Upserting is an operation that performs both: Inserting a new row if a matching row doesn't already exist. Either updating the existing row, or doing nothing, if a matching row already exists.
  • Supabase_DatabaseSelect
    • Functions:
      • Class_Globals As String
      • Columns (Column As String) As Supabase_DatabaseSelect
      • Execute As ResumableSub
      • Filter_Equal (ColumnValue As Map) As Supabase_DatabaseSelect
        Finds all rows whose value on the stated column match the specified value
      • Filter_GreatherThan (ColumnValue As Map) As Supabase_DatabaseSelect
        Finds all rows whose value on the stated column is greater than the specified value
      • Filter_GreatherThanOrEqual (ColumnValue As Map) As Supabase_DatabaseSelect
        Finds all rows whose value on the stated column is greater than or equal to the specified value
      • Filter_Ilike (ColumnValue As Map) As Supabase_DatabaseSelect
        Finds all rows whose value in the stated column matches the supplied pattern (case insensitive)
      • Filter_In (ColumnValue As Map) As Supabase_DatabaseSelect
        Finds all rows whose value on the stated column is found on the specified values
      • Filter_Is (ColumnValue As Map) As Supabase_DatabaseSelect
        A check for exact equality (null, true, false), finds all rows whose value on the stated column exactly match the specified value
      • Filter_LessThan (ColumnValue As Map) As Supabase_DatabaseSelect
        Finds all rows whose value on the stated column is less than the specified value
      • Filter_LessThanOrEqual (ColumnValue As Map) As Supabase_DatabaseSelect
        Finds all rows whose value on the stated column is less than or equal to the specified value
      • Filter_Like (ColumnValue As Map) As Supabase_DatabaseSelect
        Finds all rows whose value in the stated column matches the supplied pattern (case sensitive)
      • Filter_NotEqual (ColumnValue As Map) As Supabase_DatabaseSelect
        Finds all rows whose value on the stated column doesn't match the specified value.
      • Filter_Or (ColumnValue As Map) As Supabase_DatabaseSelect
      • Filter_TextSearch (ColumnValue As Map, FilterType As String) As Supabase_DatabaseSelect
        FilterType:
        <code>plain</code>
        <code>phrase</code>
        <code>websearch</code>
        <code>""</code>
      • From (TableName As String) As Supabase_DatabaseSelect
      • Initialize (ThisSupabase As Supabase) As String
        Initializes the object. You can add parameters to this method if needed.
      • IsInitialized As Boolean
        Tests whether the object has been initialized.
      • Range (FirstPage As Int, LastPage As Int) As Supabase_DatabaseSelect
  • Supabase_DatabaseUpdate
    • Functions:
      • Class_Globals As String
      • Eq (ColumnValue As Map) As Supabase_DatabaseUpdate
      • Execute As ResumableSub
      • From (TableName As String) As Supabase_DatabaseUpdate
      • Initialize (ThisSupabase As Supabase) As String
        Initializes the object. You can add parameters to this method if needed.
      • IsInitialized As Boolean
        Tests whether the object has been initialized.
      • Update (ColumnValue As Map) As Supabase_DatabaseUpdate
  • Supabase_Functions
    • Functions:
      • GenerateResult (j As com.stoltex.supabase.httpjob) As Map
      • getErrorCode (root As Map) As Int
        code: 400
      • getErrorMap (root As Map) As Map
        reason: invalid
        domain: global
        message: EMAIL_NOT_FOUND
      • getErrorMessage (root As Map) As String
        message: EMAIL_NOT_FOUND
      • GetFileExt (FileName As String) As String
      • GetFilename (fullpath As String) As String
      • GetMimeTypeByExtension (Extension As String) As String
      • ParseDateTime (DateString As String) As Long
      • Process_Globals As String
  • Supabase_Storage
    • Functions:
      • BytesToImage (bytes As Byte()) As B4XBitmap
      • ConvertFile2Binary (Dir As String, FileName As String) As Byte()
      • CopyFile (BucketName As String, FromPath As String, ToPath As String) As Supabase_StorageFile
        Copies an existing file to a new path in the same bucket.
        FromPath - The original file path, including the current file name. For example `folder/image.png`
        ToPath - The new file path, including the new file name. For example `folder/image-copy.png`
        <code>
        Wait For (xSupabase.Storage.CopyFile("Avatar","public/avatar1.png", "private/avatar2.png").Execute) Complete (StorageFile As SupabaseStorageFile)
        If StorageFile.Error.Success Then
        Log($"Files successfully copied "$)
        Else
        Log("Error: " & StorageFile.Error.ErrorMessage)
        End If
        </code>
      • CreateBucket (Name As String) As Supabase_StorageBucket
        Creates a new Storage bucket
        Name - A unique identifier for the bucket you are creating
        <code>
        Dim CreateBucket As Supabase_StorageBucket = xSupabase.Storage.CreateBucket("Avatar")
        CreateBucket.Options_isPublic(False)
        CreateBucket.Options_FileSizeLimit(1048576 )
        CreateBucket.Options_AllowedMimeTypes(Array("image/png","image/jpg"))
        Wait For (CreateBucket.Execute) Complete (Bucket As SupabaseStorageBucket)
        If Bucket.Error.Success Then
        Log($"Bucket ${Bucket.Name} successfully created "$)
        Else
        Log("Error: " & Bucket.Error.ErrorMessage)
        End If
        </code>
      • CreateSignedUrl (BucketName As String, Path As String, ExpiresInSeconds As Int) As Supabase_StorageFile
        Create signed url to download file without requiring permissions. This URL can be valid for a set number of seconds.
        <code>
        Wait For (xSupabase.Storage.CreateSignedUrl("Avatar","test.png",60).Execute) Complete (StorageFile As SupabaseStorageFile)
        If StorageFile.Error.Success Then
        Log(StorageFile.SignedURL)
        Dim DownloadFile As Supabase_StorageFile = xSupabase.Storage.DownloadFile("Avatar")
        DownloadFile.Path("test.png")
        DownloadFile.SignedURL(StorageFile.SignedURL)
        Wait For (DownloadFile.Execute) Complete (StorageFile As SupabaseStorageFile)
        If StorageFile.Error.Success Then
        Log($"File from signed URL successfully downloaded "$)
        ImageView1.SetBitmap(xSupabase.Storage.BytesToImage(StorageFile.FileBody))
        Else
        Log("Error: " & StorageFile.Error.ErrorMessage)
        End If
        Else
        Log("Error: " & StorageFile.Error.ErrorMessage)
        End If
        </code>
      • DeleteBucket (Name As String) As Supabase_StorageBucket
        Deletes an existing bucket. A bucket can't be deleted with existing objects inside it. You must first empty() the bucket.
        <code>
        Dim DelteBucket As Supabase_StorageBucket = xSupabase.Storage.DeleteBucket("Avatar")
        Wait For (DelteBucket.Execute) Complete (Bucket As SupabaseStorageBucket)
        If Bucket.Error.Success Then
        Log($"Bucket ${Bucket.Name} successfully deleted "$)
        Else
        Log("Error: " & Bucket.Error.ErrorMessage)
        End If
        </code>
      • DownloadFile (BucketName As String, Path As String) As Supabase_StorageFile
        Downloads a file.
        <code>
        Dim DownloadFile As Supabase_StorageFile = xSupabase.Storage.DownloadFile("Avatar","test.png")
        Wait For (DownloadFile.Execute) Complete (StorageFile As SupabaseStorageFile)
        If StorageFile.Error.Success Then
        Log($"File ${"test.jpg"} successfully downloaded "$)
        ImageView1.SetBitmap(xSupabase.Storage.BytesToImage(StorageFile.FileBody))
        Else
        Log("Error: " & StorageFile.Error.ErrorMessage)
        End If
        </code>
      • DownloadFileProgress (BucketName As String, Path As String, EventCallback As Object, EventName As String, DownloadPath As String) As Supabase_StorageFile
        <code>
        xui.SetDataFolder("supabase")
        Dim DownloadFile As Supabase_StorageFile = xSupabase.Storage.DownloadFileProgress("Avatar","test.png",Me,"DownloadProfileImage",xui.DefaultFolder)
        Wait For (DownloadFile.Execute) Complete (StorageFile As SupabaseStorageFile)
        If StorageFile.Error.Success Then
        Log($"File ${"test.jpg"} successfully downloaded "$)
        B4XImageView1.SetBitmap(xSupabase.Storage.BytesToImage(StorageFile.FileBody))
        If File.Exists(xui.DefaultFolder,"test.png") Then File.Delete(xui.DefaultFolder,"test.png") 'Clean the download path, or do what ever you want
        Else
        Log("Error: " & StorageFile.Error.ErrorMessage)
        End If
        Private Sub DownloadProfileImage_RangeDownloadTracker(Tracker As SupabaseRangeDownloadTracker)
        Log($"$1.2{Tracker.CurrentLength / 1024 / 1024}MB / $1.2{Tracker.TotalLength / 1024 / 1024}MB"$)
        AnotherProgressBar1.Value = Tracker.CurrentLength / Tracker.TotalLength * 100
        End Sub
        </code>
      • EmptyBucket (Name As String) As Supabase_StorageBucket
        Removes all objects inside a single bucket.
        <code>
        Wait For (xSupabase.Storage.EmptyBucket("Avatar").Execute) Complete (Bucket As SupabaseStorageBucket)
        If Bucket.Error.Success Then
        Log($"Bucket ${Bucket.Name} successfully cleared "$)
        Else
        Log("Error: " & Bucket.Error.ErrorMessage)
        End If
        </code>
      • GetBucket (Name As String) As Supabase_StorageBucket
        Retrieves the details of an existing Storage bucket.
        <code>
        Dim GetBucket As Supabase_StorageBucket = xSupabase.Storage.GetBucket("Avatar")
        Wait For (GetBucket.Execute) Complete (Bucket As SupabaseStorageBucket)
        If Bucket.Error.Success Then
        Log($"Bucket ${Bucket.Name} was created at ${DateUtils.TicksToString(Bucket.CreatedAt)}"$)
        Else
        Log("Error: " & Bucket.Error.ErrorMessage)
        End If
        </code>
      • GetPublicUrl (BucketName As String, Path As String) As String
        Retrieve public URL
        A simple convenience function to get the URL for an asset in a public bucket. If you do not want to use this function, you can construct the public URL by concatenating the bucket URL with the path to the asset.
        This function does not verify if the bucket is public. If a public URL is created for a bucket which is not public, you will not be able to download the asset.
        <code>Log(xSupabase.Storage.GetPublicUrl("Avatar","test.png"))</code>
      • Initialize (ThisSupabase As Supabase) As String
        Initializes the object. You can add parameters to this method if needed.
      • IsInitialized As Boolean
        Tests whether the object has been initialized.
      • MoveFile (BucketName As String, FromPath As String, ToPath As String) As Supabase_StorageFile
        Moves an existing file to a new path in the same bucket.
        FromPath - The original file path, including the current file name. For example `folder/image.png`
        ToPath - The new file path, including the new file name. For example `folder/image-copy.png`
        <code>
        Wait For (xSupabase.Storage.MoveFile("Avatar","public/avatar1.png", "private/avatar2.png").Execute) Complete (StorageFile As SupabaseStorageFile)
        If StorageFile.Error.Success Then
        Log($"Files successfully moved "$)
        Else
        Log("Error: " & StorageFile.Error.ErrorMessage)
        End If
        </code>
      • UpdateBucket (Name As String) As Supabase_StorageBucket
        Updates a new Storage bucket
        <code>
        Dim UpdateBucket As Supabase_StorageBucket = xSupabase.Storage.UpdateBucket("Avatar")
        UpdateBucket.Options_isPublic(True)
        UpdateBucket.Options_FileSizeLimit(1048576 )
        UpdateBucket.Options_AllowedMimeTypes(Array("image/png"))
        Wait For (UpdateBucket.Execute) Complete (Bucket As SupabaseStorageBucket)
        If Bucket.Error.Success Then
        Log($"Bucket ${Bucket.Name} successfully updated "$)
        Else
        Log("Error: " & Bucket.Error.ErrorMessage)
        End If
        </code>
      • UpdateFile (BucketName As String, Path As String) As Supabase_StorageFile
        Replaces an existing file at the specified path with a new one.
        <code>
        Dim UpdateFile As Supabase_StorageFile = xSupabase.Storage.UpdateFile("Avatar","test.png")
        UpdateFile.FileBody(xSupabase.Storage.ConvertFile2Binary(File.DirAssets,"test2.jpg"))
        Wait For (UpdateFile.Execute) Complete (StorageFile As SupabaseStorageFile)
        If StorageFile.Error.Success Then
        Log($"File ${"test.jpg"} successfully updated "$)
        Else
        Log("Error: " & StorageFile.Error.ErrorMessage)
        End If
        </code>
      • UploadFile (BucketName As String, Path As String) As Supabase_StorageFile
        Uploads a file to an existing bucket.
        <code>
        Dim UploadFile As Supabase_StorageFile = xSupabase.Storage.UploadFile("Avatar","test.png")
        UploadFile.FileBody(xSupabase.Storage.ConvertFile2Binary(File.DirAssets,"test.jpg"))
        Wait For (UploadFile.Execute) Complete (StorageFile As SupabaseStorageFile)
        If StorageFile.Error.Success Then
        Log($"File ${"test.jpg"} successfully uploaded "$)
        Else
        Log("Error: " & StorageFile.Error.ErrorMessage)
        End If
        </code>
  • Supabase_StorageBucket
    • Functions:
      • Class_Globals As String
      • Execute As ResumableSub
      • Initialize (ThisSupabase As Supabase, Name As String, Mode As String) As String
        Initializes the object. You can add parameters to this method if needed.
        Name - A unique identifier for the bucket
      • IsInitialized As Boolean
        Tests whether the object has been initialized.
      • Options_AllowedMimeTypes (MimeTypes As Object()) As Supabase_StorageBucket
        specifies the allowed mime types that this bucket can accept during upload. The default value is null, which allows files with all mime types to be uploaded. Each mime type specified can be a wildcard, e.g. image/*, or a specific mime type, e.g. image/png.
      • Options_FileSizeLimit (Limit As Int) As Supabase_StorageBucket
      • Options_isPublic (isPublic As Boolean) As Supabase_StorageBucket
        The visibility of the bucket. Public buckets don't require an authorization token to download objects, but still require a valid token for all other operations. By default, buckets are private.
  • Supabase_StorageFile
    • Fields:
      • Tag As Object
    • Functions:
      • Class_Globals As String
      • CopyFile (FromPath As String, ToPath As String) As Supabase_StorageFile
      • DownloadOptions_TransformFormat (Format As String) As Supabase_StorageFile
        Specify the format of the image requested. When using 'origin' we force the format to be the same as the original image. When this option is not passed in, images are optimized to modern image formats like Webp.
        <code>origin</code>
      • DownloadOptions_TransformHeight (Height As Int) As Supabase_StorageFile
        The height of the image in pixels.
      • DownloadOptions_TransformQuality (Quality As Int) As Supabase_StorageFile
        Set the quality of the returned image. A number from 20 to 100, with 100 being the highest quality. Defaults to 80
      • DownloadOptions_TransformResize (ResizeMode As String) As Supabase_StorageFile
        The resize mode can be cover, contain or fill. Defaults to cover. Cover resizes the image to maintain it's aspect ratio while filling the entire width and height. Contain resizes the image to maintain it's aspect ratio while fitting the entire image within the width and height. Fill resizes the image to fill the entire width and height. If the object's aspect ratio does not match the width and height, the image will be stretched to fit.
        <code>cover</code>
        <code>contain</code>
        <code>fill</code>
      • DownloadOptions_TransformWidth (Width As Int) As Supabase_StorageFile
        The width of the image in pixels.
      • Execute As ResumableSub
      • ExpiresInSeconds (Seconds As Int) As Supabase_StorageFile
      • FileBody (Data As Byte()) As Supabase_StorageFile
      • Initialize (ThisSupabase As Supabase, BucketName As String, Mode As String) As String
        Initializes the object. You can add parameters to this method if needed.
      • IsInitialized As Boolean
        Tests whether the object has been initialized.
      • MoveFile (FromPath As String, ToPath As String) As Supabase_StorageFile
      • Options_CacheControl (CacheControl As Int) As Supabase_StorageFile
        Only for UploadFile and UpdateFile
        The number of seconds the asset is cached in the browser and in the Supabase CDN. This is set in the `Cache-Control: max-age=<seconds>` header. Defaults to 3600 seconds.
      • Options_Upsert (isUpsert As Boolean) As Supabase_StorageFile
        Only for UploadFile and UpdateFile
        When upsert is set to true, the file is overwritten if it exists. When set to false, an error is thrown if the object already exists. Defaults to false.
      • Path (FileName As String) As Supabase_StorageFile
        The file path, including the file name. Should be of the format `folder/subfolder/filename.png`. The bucket must already exist before attempting to upload.
      • RangeDownloader_CreateTracker As SupabaseRangeDownloadTracker
      • RangeDownloader_Download (Dir As String, FileName As String, URL As String, Tracker As SupabaseRangeDownloadTracker) As ResumableSub
      • Remove (FileNames As Object()) As Supabase_StorageFile
      • SignedURL (Url As String) As Supabase_StorageFile
Setup
B4X:
Private xSupabase As Supabase
xSupabase.Initialize("YOUR_SUPABASE_URL","YOUR_SUPABASE_ANON_KEY")
xSupabase.InitializeEvents(Me,"Supabase")
Changelog
  • 1.00
    • Release
  • 1.01 (read more)
    • BugFixes
    • Add InitializeEvents
    • Add Event AuthStateChange
    • StateType
      • passwordRecovery, signedIn, signedOut, tokenRefreshed, userUpdated
  • 1.02
    • General
      • BugFixes
    • Auth
      • Add SignInWithOAuth - Signs the user in using third party OAuth providers
        • Adds Google
      • Add Enum Provider_Google
        • so you always know which providers are already implemented
        • xSupabase.Auth.Provider_Google
  • 1.03
    • General
      • BugFixes
    • Auth
      • Add SignInWithOAuth with the Apple Provider
      • Add xSupabase.Auth.Provider_Apple - B4I only
  • 1.04
    • General
      • BugFixes
    • Auth
      • Add isUserLoggedIn - Checks if the user is logged in, renews the access token if it has expired
  • 1.05
    • Storage
      • CRUD operations with buckets can now be performed
      • CRUD operations with files can now be performed
  • 1.06
    • Storage
      • BugFixes
      • Add DownloadFileProgress - Download large files with a progress indicator
        • DownloadFileProgress uses http range feature to download the file in chunks. It will resume the download from the previous point, even if the app was previously killed.
        • It first sends a HEAD request to test whether this feature is supported.
        • Note that you need to delete the target file if you want to restart the download.
  • 1.07
    • Storage
      • Add DownloadOptions_Transform...
        • height, width, resize,format,quality
  • 1.08
    • BugFixes
  • 1.09
    • Realtime
      • Add SupabaseRealtime
        • You can now subscribe to topics and get database changes in real time
  • 1.10
    • Database
      • RLS and Auth error message removed when no rows are present
      • Removed unnecessary log messages
    • Realtime
      • Works now with B4J
      • Add get isConnected - Returns true if the websocket is connected to the database
      • Removed unnecessary log messages
      • BugFixes
  • 1.11 (read more)
    • Auth
      • Add the "Options" parameter to the SignUp function
        • sign up with additional user metadata
      • Add "Metadata" to SupabaseUser
      • BugFixes
    • Database
      • Select - Joins are now supportet
  • 1.12
    • Realtime
      • Complete workflow redesigned to be closer to the official libraries
      • Add Filters
        • Filter_Equal, Filter_NotEqual, Filter_GreatherThan, Filter_GreatherThanOrEqual, Filter_LessThan, Filter_LessThanOrEqual, Filter_In
      • Add SubscribeType - Broadcast
        • Send ephemeral messages from client to clients with low latency.
      • Add SubscribeType - Presence
        • Track and synchronize shared state between clients.
      • Add SubscribeType - PostgresChanges
        • Listen to Postgres database changes and send them to authorized clients.
  • 1.13 (read more)
    • General
      • Add get and set LogEvents - If true then you get debugging infos in the log
      • Add Some infos to log messages
    • Auth
      • BugFix
    • Database
      • Add OrderBy
      • Add Limit
      • Add Offset
  • 1.14
    • Compatibility for server applications
  • 1.15
    • Realtime
      • Add support for Presence and Broadcast
        • Presence: Share state between users with Realtime Presence.
        • Broadcast: Send and receive messages using Realtime Broadcast
      • Add new enums
        • get Event_Sync - Presence only
        • get Event_Join - Presence only
        • get Event_Leave - Presence only
      • Add support for multi event subscribe
      • Add SendBroadcast
      • Add Event BroadcastDataReceived
      • Add Event PresenceDataReceived
  • 1.16
    • Auth
      • BugFixes
    • Realtime
      • Add Event Disconnected
  • 1.17
    • Removes the dependency of the xui library so that the library can also work in server apps (non ui)
  • 1.18
    • Database
      • Filter Ilike BugFix
  • 1.19 (read more)
    • Database
      • Add SelectData to INSERT - Create a record and return it
      • Add SelectData to UPDATE - Update a record and return it
      • BreakingChange on Supabase_DatabaseInsert
        • The return value for execute is no longer of type SupabaseError it is now SupabaseDatabaseResult
      • BreakingChange on Supabase_DatabaseUpdate
        • The return value for execute is no longer of type SupabaseError it is now SupabaseDatabaseResult
  • 1.20
    • Database
      • Support for json columns
        • the json string of the column must look like this to be recognized: [{"name":"Volleyball","id":1}]
          • Important that it starts with [ and ends with ]
  • 1.21
    • Auth
      • BugFix - in GetUser, if the retrieval was successful, the data set was still marked as "False" in the error object
  • 1.22
    • Auth
      • BugFixes on SignUp
      • Add LogIn_Anonymously - Allow your users to sign up without requiring users to enter an email address, password
      • Add isAnonymous to SupabaseUser
      • BugFixes on oAuth
  • 1.23
    • Database
      • Add rpc support - Call a Postgres function
      • BugFixes
Have Fun :)
 

Attachments

  • Supabase.b4xlib
    34 KB · Views: 147
Last edited:

Mashiane

Expert
Licensed User
Longtime User
what do you mean?
Apparently, one is able to (a) have their supabase database resising on supabase hosting and (b) can self host their own supabase database.

The link I sent above is in relation to (b) and because you have gracefully taken us through this, with time permitting, if perhaps you could also look into it. That is my request.

Thanks.

PS: Its just a matter of having more options available when one wants to deploy their back-end with self hosting / leave it at supabase cloud?
 

Waldemar Lima

Well-Known Member
Licensed User
Longtime User
It would be interesting to add realtime("websocket"), because it might be possible to add "data processing" within realtime("websocket") using b4j.
 

Alexander Stolte

Expert
Licensed User
Longtime User
It would be interesting to add realtime("websocket"), because it might be possible to add "data processing" within realtime("websocket") using b4j.
If you know how to connect supabase with websockets feel free to tell me, I have no idea about it yet.
 

Waldemar Lima

Well-Known Member
Licensed User
Longtime User
here are some explanations:

by using the js api, I can get the useful links of some modules in supabase.
including realtime...

B4X:
wss://YOURREFERENCEID.supabase.co/realtime/v1/websocket?apikey=YOURAPIKEY&vsn=1.0.0

{
    "accessToken": "YOUR PROJECT APIKEY",
    "channels": [],
    "endPoint": "wss://xxxxxxxxxxx Project Reference ID xxxxxxxxxxxxxxx.supabase.co/realtime/v1/websocket",
    "headers": {
        "X-Client-Info": "supabase-js-web/2.38.0"
    },
    "params": {
        "apikey": "xxxxxxxxxxxxx YOUR PROJECT APIKEY xxxxxxxxxxxxxxx"
    },
    "timeout": 10000,
    "heartbeatIntervalMs": 30000,
    "pendingHeartbeatRef": null,
    "ref": 0,
    "conn": null,
    "sendBuffer": [],
    "serializer": {
        "HEADER_LENGTH": 1
    },
    "stateChangeCallbacks": {
        "open": [],
        "close": [],
        "error": [],
        "message": []
    },
    "eventsPerSecondLimitMs": 100,
    "inThrottle": false,
    "reconnectTimer": {
        "tries": 0
    }
}

Messages:
1696987238569.png
 
Last edited:

Alexander Stolte

Expert
Licensed User
Longtime User
Will this be useful?
yes, but it does not work with the websocket library of b4j.
 

Alexander Stolte

Expert
Licensed User
Longtime User
I am currently working on the realtime class for Supabase. Currently only for B4A and B4I, because the websocket library in B4J with Java 11 is currently not compatible with Supabase.

I have already been able to receive RealTime messages and process them in the class. Now my challenge is to include the Auth class so that the whole thing also works with RLS enabled.
 

Waldemar Lima

Well-Known Member
Licensed User
Longtime User
That's excellent news...
It's a shame that it's still not possible in b4j :c

I'm available to help with whatever you need..
 

Alexander Stolte

Expert
Licensed User
Longtime User
Update
  • 1.09
    • Realtime
      • Add SupabaseRealtime
        • You can now subscribe to topics and get database changes in real time
 

Alexander Stolte

Expert
Licensed User
Longtime User
Update
  • 1.10
    • Database
      • RLS and Auth error message removed when no rows are present
      • Removed unnecessary log messages
    • Realtime
      • Works now with B4J
      • Add get isConnected - Returns true if the websocket is connected to the database
      • Removed unnecessary log messages
      • BugFixes
 

Alexander Stolte

Expert
Licensed User
Longtime User
Update
  • 1.11
    • Auth
      • Add the "Options" parameter to the SignUp function
        • sign up with additional user metadata
      • Add "Metadata" to SupabaseUser
      • BugFixes
    • Database
      • Select - Joins are now supportet
Auth - SignUp options parameter
The options parameters are used to store additional information, e.g. user name, birthday, etc. in the user record when registering new users.
B4X:
Dim AdditionalUserMetadata As Map = CreateMap("first_name":"Alexander","age":25)
Wait For (xSupabase.Auth.SignUp("[email protected]","Test123!",AdditionalUserMetadata)) Complete (NewUser As SupabaseUser)
1698361060596.png

This information is then stored in the "raw_user_meta_data" column in the Auth.Users table.
"Metadata" to SupabaseUser
To retrieve this info from a user, you can get the user object:
B4X:
Wait For (xSupabase.Auth.GetUser) Complete (User As SupabaseUser)
Log(User.Metadata.Get("username"))
Joins
In the following example I make a join into the "public.users" table and need the column "username" from it.
B4X:
    Dim Query As Supabase_DatabaseSelect = xSupabase.Database.SelectData
    Query.Columns("message,created_by,id, users(username)")
    Query.From("dt_Chat")
    Query.Filter_Equal(CreateMap("room_id":3))

    Wait For (Query.Execute) Complete (DatabaseResult As SupabaseDatabaseResult)
    
    For Each Row As Map In  DatabaseResult.Rows
        
        AddItem(Row.Get("message"), Row.Get("created_by") = User.Id,Row.Get("users.username"))
        
    Next
I can query the joined column with Row.Get("users.username")
 

Alexander Stolte

Expert
Licensed User
Longtime User
Update
  • 1.12
    • Realtime
      • Complete workflow redesigned to be closer to the official libraries
      • Add Filters
        • Filter_Equal, Filter_NotEqual, Filter_GreatherThan, Filter_GreatherThanOrEqual, Filter_LessThan, Filter_LessThanOrEqual, Filter_In
      • Add SubscribeType - Broadcast
        • Send ephemeral messages from client to clients with low latency.
      • Add SubscribeType - Presence
        • Track and synchronize shared state between clients.
      • Add SubscribeType - PostgresChanges
        • Listen to Postgres database changes and send them to authorized clients.
Realtime tutorial updated
B4X:
Realtime _
.Channel("public","dt_Chat",Realtime.BuildFilter("room_id",Realtime.Filter_Equal,"3")) _
.On(Realtime.SubscribeType_PostgresChanges) _
.Event(Realtime.Event_ALL) _
.Subscribe
 

Alexander Stolte

Expert
Licensed User
Longtime User
I don't understand what the filters on the channel are for
Is this to create "rooms" with specific characteristics?
have a look at the realtime tutorial
 
Top