esupa is an Erlang/OTP library that provides a comprehensive client interface for Supabase, supporting both REST API operations and real-time WebSocket connections for PostgreSQL change streams.
- Complete CRUD Operations: SELECT, INSERT, UPDATE, DELETE
- Advanced Querying: Filters, joins, ordering, pagination
- Connection Pooling: Efficient HTTP client management
- Schema Support: Multi-schema database operations
- Type Safety: Comprehensive type specifications
- PostgreSQL Change Streams: Listen to database changes in real-time
- Automatic Reconnection: Robust connection handling with failover
- Event Filtering: Subscribe to specific tables, schemas, and operations
- Connection Pooling: Multiple WebSocket connection management
- Heartbeat Support: Automatic connection health monitoring
Add esupa
to your mix.exs
dependencies:
defp deps do
[
{:esupa, git: "https://github.com/ditas/esupa.git", tag: "0.6.2"}
]
Configure your Supabase connection:
config :esupa,
env: :prod,
esupa_logger_level: :error,
http_handler_pool_size: 10,
project_url: <YOUR_PROJECT>,
base_url: ~c".supabase.co",
rest_url: ~c"/rest/v1/",
key: <YOUR-ANON-OR-SERVICE_KEY>,
ws_url: ~c"/realtime/v1/websocket",
max_ws_handler_pool_size: 10,
httpc_options: [
keep_alive_timeout: 100,
max_pipeline_length: 100,
max_sessions: 100
]
# Get a client connection
{:ok, client} = :esupa.get_client()
# Build and execute a query
result = :esupa.request(client, get, 'public')
|> :esupa.supa_from('users')
|> :esupa.supa_select(['id', 'name', 'email'])
|> :esupa.supa_eq('active', true)
|> :esupa.supa_order('created_at', desc)
|> :esupa.supa_range(0, 10)
|> :esupa.execute()
# Subscribe to table changes
:esupa_websocket_service.subscribe(
self(), # Receiver PID
{'public', 'users', 'INSERT', ''}, # {schema, table, event, filter}
1 # Number of subscribers
)
# Handle incoming changes
receive
%{"event" => "postgres_changes", "payload" => payload} ->
do_something(payload)
end
Retrieves an available HTTP client from the connection pool.
{ok, Client} = esupa:get_client().
Creates a new request targeting the specified schema.
Request = esupa:request(Client, get, "public").
Parameters:
Client
: HTTP client PIDMethod
: HTTP method (get
,post
,patch
,delete
)Schema
: Database schema name
Specifies the target table for the operation.
Request2 = esupa:supa_from(Request, "users").
Defines which columns to return. Use []
for all columns.
%% Select specific columns
Request3 = esupa:supa_select(Request2, ["id", "name", "email"]),
%% Select all columns
Request3 = esupa:supa_select(Request2, []).
Adds JOIN operations with related tables.
%% Join with profiles table
Joins = [{"profiles", ["avatar_url", "bio"]}],
Request4 = esupa:supa_join(Request3, Joins).
%% Equality
Request = esupa:supa_eq(Request, "status", "active"),
%% Greater than
Request = esupa:supa_gt(Request, "age", 18),
%% Greater than or equal
Request = esupa:supa_gte(Request, "score", 100),
%% Less than
Request = esupa:supa_lt(Request, "price", 50),
%% Less than or equal
Request = esupa:supa_lte(Request, "quantity", 10),
%% IN clause
Request = esupa:supa_in(Request, "category", ["electronics", "books"]).
%% OR conditions
OrConditions = [
{"age", "gte", "21"},
{"verified", "eq", "true"}
],
Request = esupa:supa_or(Request, OrConditions).
Orders results by the specified column.
%% Ascending order
Request = esupa:supa_order(Request, "created_at", asc),
%% Descending order
Request = esupa:supa_order(Request, "updated_at", desc).
Implements pagination by limiting the result range.
%% Get records 0-19 (first page, 20 items)
Request = esupa:supa_range(Request, 0, 19),
%% Get records 20-39 (second page, 20 items)
Request = esupa:supa_range(Request, 20, 39).
Executes the built request and returns the response.
Result = esupa:execute(Request).
Subscribes to real-time database changes.
Parameters:
ReceiverPid
: Process that will receive change notificationsRequest
: Tuple of{Schema, Table, Event, Filter}
NumSubscribers
: Number of WebSocket connections to establish
Events:
"INSERT"
: New records created"UPDATE"
: Existing records modified"DELETE"
: Records deleted"*"
: All change events
Example:
%% Listen to all INSERT events on users table
esupa_websocket_service:subscribe(
self(),
{"public", "users", "INSERT", ""},
1
),
%% Listen to UPDATE events for specific user
esupa_websocket_service:subscribe(
self(),
{"public", "users", "UPDATE", "id=eq.123"},
1
).
{esupa, [
{base_url, ".supabase.co"},
{project_url, "<YOUR_PROJECT>"}.
{rest_url, "/rest/v1/"},
{key, "<YOUR-ANON-OR-SERVICE_KEY>"},
{http_handler_pool_size, 10},
{httpc_options, [
{keep_alive_timeout, 120000},
{max_pipeline_length, 10},
{max_sessions, 20}
]}
]}
{esupa, [
{ws_url, "/realtime/v1/websocket"},
{max_ws_handler_pool_size, 5}
]}
$ rebar3 compile
$ rebar3 shell
# Local development release
$ rebar3 as local release
$ ./_build/local/rel/esupa/bin/esupa console
# Test environment
$ rebar3 as test release
$ ./_build/test/rel/esupa/bin/esupa console
# Production release
$ rebar3 as prod release
$ ./_build/prod/rel/esupa/bin/esupa daemon
TBD
Generate documentation using ExDoc:
$ rebar3 ex_doc
This project is licensed under the Apache License 2.0 - see the LICENSE.md file for details.
- Erlang/OTP 25+: Required runtime
- gun: HTTP/WebSocket client
- jsx: JSON encoding/decoding
- recon: Production debugging tools
- Issues: GitHub Issues
- Documentation: Generated docs available in
/docs
(TBD) - Examples: See
/examples
directory for more use cases (TBD)
Made with ❤️ for the Erlang and Supabase communities.