Skip to content

Commit 32860ec

Browse files
authored
Merge pull request #6 from powersync-ja/supabase-connector
Added Supabase Connector
2 parents 7576512 + ad1b455 commit 32860ec

File tree

12 files changed

+455
-10
lines changed

12 files changed

+455
-10
lines changed

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,8 @@ TestResults/
6161
*.dylib
6262
*.dll
6363
*.so
64+
65+
.env
66+
67+
# Ignore user id file
68+
user_id.txt

demos/CommandLine/.env.template

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# PowerSync server URL
2+
POWERSYNC_URL=http://localhost:8080
3+
4+
# Set to true if you want to use Supabase as the backend
5+
# Set to false if you want to use the PowerSync self-hosted backend
6+
USE_SUPABASE=false
7+
8+
# --- Supabase Connector Settings ---
9+
# These values are used only if USE_SUPABASE=true
10+
11+
# Supabase project URL
12+
SUPABASE_URL=http://localhost:54321
13+
14+
# Supabase anon key (public client access)
15+
SUPABASE_ANON_KEY=your_anon_key_here
16+
17+
# Supabase credentials for an already existing user (used for login)
18+
SUPABASE_USERNAME=your_supabase_email@example.com
19+
SUPABASE_PASSWORD=your_supabase_password
20+
21+
# --- PowerSync Backend Settings ---
22+
# These values are used only if USE_SUPABASE=false
23+
24+
# URL of your PowerSync self-hosted backend
25+
BACKEND_URL=http://localhost:6060

demos/CommandLine/CommandLine.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@
1313
</PropertyGroup>
1414

1515
<ItemGroup>
16+
<PackageReference Include="Dotenv.Net" Version="3.2.1" />
1617
<PackageReference Include="Microsoft.Data.Sqlite" Version="9.0.1" />
1718
<PackageReference Include="Spectre.Console" Version="0.49.1" />
19+
<PackageReference Include="supabase" Version="1.1.1" />
1820

1921
</ItemGroup>
2022

demos/CommandLine/CommandLine.sln

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
Microsoft Visual Studio Solution File, Format Version 12.00
2+
# Visual Studio Version 17
3+
VisualStudioVersion = 17.5.2.0
4+
MinimumVisualStudioVersion = 10.0.40219.1
5+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommandLine", "CommandLine.csproj", "{6BB9F16E-3825-DE76-1286-9E5E2406710D}"
6+
EndProject
7+
Global
8+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
9+
Debug|Any CPU = Debug|Any CPU
10+
Release|Any CPU = Release|Any CPU
11+
EndGlobalSection
12+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
13+
{6BB9F16E-3825-DE76-1286-9E5E2406710D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
14+
{6BB9F16E-3825-DE76-1286-9E5E2406710D}.Debug|Any CPU.Build.0 = Debug|Any CPU
15+
{6BB9F16E-3825-DE76-1286-9E5E2406710D}.Release|Any CPU.ActiveCfg = Release|Any CPU
16+
{6BB9F16E-3825-DE76-1286-9E5E2406710D}.Release|Any CPU.Build.0 = Release|Any CPU
17+
EndGlobalSection
18+
GlobalSection(SolutionProperties) = preSolution
19+
HideSolutionNode = FALSE
20+
EndGlobalSection
21+
GlobalSection(ExtensibilityGlobals) = postSolution
22+
SolutionGuid = {A5588511-5909-4F05-80EB-09A56805607C}
23+
EndGlobalSection
24+
EndGlobal

demos/CommandLine/Demo.cs

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
namespace CommandLine;
22

3+
using CommandLine.Utils;
34
using PowerSync.Common.Client;
5+
using PowerSync.Common.Client.Connection;
46
using Spectre.Console;
57

68
class Demo
79
{
8-
910
private record ListResult(string id, string name, string owner_id, string created_at);
1011
static async Task Main()
1112
{
@@ -16,7 +17,31 @@ static async Task Main()
1617
});
1718
await db.Init();
1819

19-
var connector = new NodeConnector();
20+
var config = new Config();
21+
22+
IPowerSyncBackendConnector connector;
23+
24+
string connectorUserId = "";
25+
26+
if (config.UseSupabase)
27+
{
28+
var supabaseConnector = new SupabaseConnector(config);
29+
30+
// Ensure this user already exists
31+
await supabaseConnector.Login(config.SupabaseUsername, config.SupabasePassword);
32+
33+
connectorUserId = supabaseConnector.UserId;
34+
35+
connector = supabaseConnector;
36+
}
37+
else
38+
{
39+
var nodeConnector = new NodeConnector(config);
40+
41+
connectorUserId = nodeConnector.UserId;
42+
43+
connector = nodeConnector;
44+
}
2045

2146
var table = new Table()
2247
.AddColumn("id")
@@ -60,7 +85,7 @@ static async Task Main()
6085
}
6186
else if (key.Key == ConsoleKey.Enter)
6287
{
63-
await db.Execute("insert into lists (id, name, owner_id, created_at) values (uuid(), 'New User', ?, datetime())", [connector.UserId]);
88+
await db.Execute("insert into lists (id, name, owner_id, created_at) values (uuid(), 'New User', ?, datetime())", [connectorUserId]);
6489
}
6590
else if (key.Key == ConsoleKey.Backspace)
6691
{
@@ -88,7 +113,6 @@ static async Task Main()
88113
}
89114
});
90115

91-
92116
// Start live updating table
93117
await AnsiConsole.Live(panel)
94118
.StartAsync(async ctx =>
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
namespace CommandLine.Helpers;
2+
3+
using System.Linq.Expressions;
4+
using Newtonsoft.Json;
5+
using Supabase.Postgrest.Interfaces;
6+
using Supabase.Postgrest.Models;
7+
8+
public static class SupabasePatchHelper
9+
{
10+
// Applies a "SET" operation to the table, setting the value of a specific property.
11+
public static IPostgrestTable<T> ApplySet<T>(
12+
IPostgrestTable<T> table, // The table to apply the operation to
13+
string jsonPropertyName, // The name of the JSON property to update
14+
object value // The new value to set for the property
15+
) where T : BaseModel, new() // Ensures T is a subclass of BaseModel with a parameterless constructor
16+
{
17+
// Find the property on the model that matches the JSON property name
18+
var property = typeof(T)
19+
.GetProperties() // Get all properties of the model type
20+
.FirstOrDefault(p =>
21+
// Check if the property has a JsonPropertyAttribute
22+
p.GetCustomAttributes(typeof(JsonPropertyAttribute), true)
23+
.FirstOrDefault() is JsonPropertyAttribute attr &&
24+
attr.PropertyName == jsonPropertyName); // Check if the JSON property name matches
25+
26+
if (property == null)
27+
throw new ArgumentException($"'{jsonPropertyName}' is not a valid property on type '{typeof(T).Name}'");
28+
29+
// Create an expression to access the specified property on the model
30+
var parameter = Expression.Parameter(typeof(T), "x"); // Define a parameter for the expression
31+
var propertyAccess = Expression.Property(parameter, property.Name); // Access the property
32+
var converted = Expression.Convert(propertyAccess, typeof(object)); // Convert the value to object type
33+
var lambda = Expression.Lambda<Func<T, object>>(converted, parameter); // Create a lambda expression for the property
34+
35+
// Apply the "SET" operation to the table using the lambda expression
36+
return table.Set(lambda, value);
37+
}
38+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
2+
using Newtonsoft.Json;
3+
using Supabase.Postgrest.Attributes;
4+
using Supabase.Postgrest.Models;
5+
6+
namespace CommandLine.Models.Supabase;
7+
8+
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.
9+
[Table("lists")]
10+
class List : BaseModel
11+
{
12+
[PrimaryKey("id")]
13+
[JsonProperty("id")]
14+
public string Id { get; set; }
15+
16+
[Column("created_at")]
17+
[JsonProperty("created_at")]
18+
public string CreatedAt { get; set; }
19+
20+
[Column("name")]
21+
[JsonProperty("name")]
22+
public string Name { get; set; }
23+
24+
[Column("owner_id")]
25+
[JsonProperty("owner_id")]
26+
public string OwnerId { get; set; }
27+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
2+
using Microsoft.VisualBasic;
3+
using Newtonsoft.Json;
4+
using Supabase.Postgrest.Attributes;
5+
using Supabase.Postgrest.Models;
6+
7+
namespace CommandLine.Models.Supabase;
8+
9+
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.
10+
[Table("todos")]
11+
class Todo : BaseModel
12+
{
13+
[PrimaryKey("id")]
14+
[JsonProperty("id")]
15+
public string Id { get; set; }
16+
17+
[Column("list_id")]
18+
[JsonProperty("list_id")]
19+
public string ListId { get; set; }
20+
21+
[Column("created_at")]
22+
[JsonProperty("created_at")]
23+
public string CreatedAt { get; set; }
24+
25+
[Column("completed_at")]
26+
[JsonProperty("completed_at")]
27+
public string CompletedAt { get; set; }
28+
29+
[Column("description")]
30+
[JsonProperty("description")]
31+
public string Description { get; set; }
32+
33+
[Column("created_by")]
34+
[JsonProperty("created_by")]
35+
public string CreatedBy { get; set; }
36+
37+
[Column("completed_by")]
38+
[JsonProperty("completed_by")]
39+
public string CompletedBy { get; set; }
40+
41+
[Column("completed")]
42+
[JsonProperty("completed")]
43+
public int Completed { get; set; }
44+
}

demos/CommandLine/NodeConnector.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
using PowerSync.Common.Client;
1111
using PowerSync.Common.Client.Connection;
1212
using PowerSync.Common.DB.Crud;
13-
13+
using CommandLine.Utils;
1414

1515
public class NodeConnector : IPowerSyncBackendConnector
1616
{
@@ -22,15 +22,15 @@ public class NodeConnector : IPowerSyncBackendConnector
2222
public string UserId { get; private set; }
2323
private string? clientId;
2424

25-
public NodeConnector()
25+
public NodeConnector(Config config)
2626
{
2727
_httpClient = new HttpClient();
2828

2929
// Load or generate User ID
3030
UserId = LoadOrGenerateUserId();
3131

32-
BackendUrl = "http://localhost:6060";
33-
PowerSyncUrl = "http://localhost:8080";
32+
BackendUrl = config.BackendUrl;
33+
PowerSyncUrl = config.PowerSyncUrl;
3434

3535
clientId = null;
3636
}

demos/CommandLine/README.md

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# PowerSync CLI demo app
1+
# PowerSync CLI Demo App
22

33
This demo features a CLI-based table view that stays *live* using a *watch query*, ensuring the data updates in real time as changes occur.
44
To run this demo, you need to have one of our Node.js self-host demos ([Postgres](https://github.com/powersync-ja/self-host-demo/tree/main/demos/nodejs) | [MongoDB](https://github.com/powersync-ja/self-host-demo/tree/main/demos/nodejs-mongodb) | [MySQL](https://github.com/powersync-ja/self-host-demo/tree/main/demos/nodejs-mysql)) running, as it provides the PowerSync server that this CLI's PowerSync SDK connects to.
@@ -9,6 +9,36 @@ Changes made to the backend's source DB or to the self-hosted web UI will be syn
99

1010
This essentially uses anonymous authentication. A random user ID is generated and stored in local storage. The backend returns a valid token which is not linked to a specific user. All data is synced to all users.
1111

12+
> **Note for Supabase users:**
13+
> If you are using `USE_SUPABASE=true`, this demo expects a valid, **already existing Supabase user**.
14+
> You must provide their credentials via the `.env` file using `SUPABASE_USERNAME` and `SUPABASE_PASSWORD`.
15+
16+
## Connection Options
17+
18+
By default, this demo uses the NodeConnector for connecting to the PowerSync server. However, you can swap this out with the SupabaseConnector if needed
19+
20+
1. Copy the `.env.template` file to a new `.env` file:
21+
```bash
22+
# On Linux/macOS
23+
cp .env.template .env
24+
25+
# On Windows
26+
copy .env.template .env
27+
```
28+
29+
2. Replace the necessary fields in the `.env` file with your Supabase and PowerSync credentials:
30+
```
31+
SUPABASE_URL=your-supabase-url
32+
SUPABASE_ANON_KEY=your_anon_key_here
33+
POWERSYNC_URL=your-powersync-url
34+
BACKEND_URL=your-backend-url
35+
SUPABASE_USERNAME=your-supabase-username
36+
SUPABASE_PASSWORD=your-supabase-password
37+
# Set to true if you want to use Supabase as the backend
38+
# Set to false if you want to use the Powersync backend
39+
USE_SUPABASE=false
40+
```
41+
1242
## Getting Started
1343

1444
In the repo root, run the following to download the PowerSync extension:
@@ -29,4 +59,4 @@ To run the Command-Line interface:
2959

3060
```bash
3161
dotnet run Demo
32-
```
62+
```

0 commit comments

Comments
 (0)