Supabase plugin
Supabase and Legend-State work very well together - all you need to do is provide a typed client and the observables will be fully typed and handle calling the correct action functions for you.
Full Example
We’ll start with a full example to see what a full setup looks like, then go into specific details.
Set up Supabase types
The first step to getting strongly typed observables from Supabase is to follow their instructions to create a typed client.
https://supabase.com/docs/guides/api/rest/generating-types
The examples on this page will use the supabase
client from the generated types:
filter
By default it will use select()
on the collection. If you want to filter the data, use the filter
parameter. See the Using Filters docs for details.
select
By default it will use select()
on the collection. If you want to be more specific, use the select
parameter to customize how you want to select. See the Select docs for details.
You can also add filters here if you want.
actions
By default it will support create, read, update, and delete. But you can specify which actions you want to support with the actions
parameter.
as
The shape of the observable object can be changed with the as
parameter, which supports three options:
object
: The default, an object keyed by the row’sid
field.Map
: A Map, which can be more efficient for accessing rows by keyvalue
: Treat the result of a query as a single value like aget
Note that array
is not an option because arrays make it hard to to efficiently and correctly add, update, and remove elements by id.
Realtime
Enable realtime on the observable with the realtime
option. This will update the observable immediately whenever any realtime changes come in. You can optionally set the schema
and filter
for the realtime listener.
See Supabase’s Realtime Docs for more details about realtime filters.
RPC and Edge Functions
You can override any or all of the default list/create/update/delete actions with an rpc or function call. There is just one requirement: create and update need to return either full row data or nothing, because the returned data is used to update the observable with any fields changed remotely (like updated_at).
One caveat is that Supabase’s edge functions are not strongly typed so the observable can’t infer the type from it.
Sync only diffs
An optional but very useful feature is the changesSince: 'last-sync'
option. This can massively reduce badwidth usage when you’re persisting list results since it only needs to list changes since the last query. The way this works internally is basically:
- Save the maximum updatedAt to the local persistence
- In subsequent syncs or after refresh it will list by
updated_at: lastSync + 1
to get only recent changes - The new changes will be merged into the observable
Enabling this on the Supabase side requires adding created_at
and updated_at
columns and a trigger to your table. You can run this snippet to set it up, just replace the two instances of YOUR_TABLE_NAME.
And to enable this feature in Legend-State, use the changesSince
option and set the fieldCreatedAt
and fieldUpdatedAt
options to match the Supabase column names.
Soft deletes
The delete parameter does not need to be an actual delete
action in Supabase. You could also implement it as a soft delete if you prefer, just setting a deleted
field to true. To do that you can provide fieldDeleted
matching the field name in your table.
Then when you delete an element it will internally update the row with { deleted: true }
instead of deleting it, and the list action will remove deleted elements from the observable.