I’m developing a Supabase client for Elixir with complete support for all their services, like:
- storage
- auth
- realtime
- UI (live view)
So there’s a “parent” module that define clients management/initialisation and then child module for each integration, like:
Supabase
Supabase.Storage
Supabase.Auth
Supabase.UI
Supabase.PostgREST
The problem is: the user can possibly needs only the Supabase
and Supabase.Storage
modules and not the others, for example. So how I could “activate” and “deactivate” the rest of modules? Does it makes sense to compile all children modules to only use one of them?
So I started to search some possibilities:
- Use adapters
Like bamboo
and Ecto
, they implement adapters and you choose which you want to use. but this options doesn’t seems to match my requirements as the children modules aren’t “a different implementation for the same final result”, they are completely different “applications”
- Conditionally compile modules
Given a config like
config :supabase, :storage, enable: true
config :supabase, :realtime, enable: true
I would define these modules as
if Application.get_env(:supabase, [:storage, :enable]) do
defmodule Supabase.Storage do
# ...
end
end
if Application.get_env(:supabase, [:realtime, :enable]) do
defmodule Supabase.Realtime do
# ...
end
end
The negative side is that I would need to set this if/2
on every child/helper module and also it doesn’t seems to be a very good practice for libraries/elixir applications
- Define “clients”/features on compile time
As Ecto
do with the Ecto.Repo
config, it would be necessary to define your own Storage
or Realtime
modules and then use their implementation like
defmodule MyApp.SupabaseStorage do
use Supabase.Storage # , config: ...
end
But it seems kinda strange use case for API integrations and is like overusing compile time features
- split into multiple packages
The last one I thought is to split those children modules into separated libraries that requires the “parent” one like ex_aws
does. So if you want to use Supabase.Storage
you would need:
# mix.exs
defp deps do
# …
[{:supabase_potion, “~> x.x.x“}, {:supabase_storage, “~> x.x.x"}]
# ...
end
The downside is to maintain multiples packages/repositories
Conclusion
So I would like to ask for the community: what it makes more sense? Which is the best alternative in your opinion? There are another options to solve this “problem”? How do you would proceed in this situation?