Photo by Caspar Camille Rubin on Unsplash

Using Supabase’s HTTP endpoints in an Elm app

azurewaters
4 min readSep 30, 2022

--

Overview

There are two ways for your app to access Supabase — through their “API” and through their “API”. Let me explain:

One: Supabase can be accessed through a traditional REST API. With these, you can use the equivalents of JavaScript’s “fetch” command. You can see the URLs you need listed in the “Bash” sub-section of the API section of your Supabase project’s control panel.

READ ALL ROWScurl '<https://abcdefghij.supabase.co/rest/v1/reports?select=*>' \\
-H "apikey: SUPABASE_KEY" \\
-H "Authorization: Bearer SUPABASE_KEY"

Two: Supabase also provides you with libraries, including one in JavaScript, which abstract away the fetch commands. You can learn how to access your data through the “JavaScript” sub-section of the API section of your control panel. With the library, all you need to do is specify the operation and its parameters. This saves you from repeatedly specifying the fetch commands, the URL and the credentials.

Screenshot of code fetching all columns from the reports table using Supabase’s library.

I chose to do my last side-project using the REST API to access Supabase. My reasoning was:

  1. Less code in JavaScript. I thought that if all the code was in Elm, there’d be less chances of unhandled errors. This held true even at the end of the project.
  2. Lesser number of decoders. It was silly of me to expect I’d be writing fewer decoders in Elm if I chose the HTTP endpoints — they’d be the same no matter the method of access.
  3. Less boilerplate code. I did end up writing less boilerplate code — there was no need to set up outgoing ports, no need to subscribe to them in JavaScript, and no need to subscribe to incoming ports’ data within Elm.

Making HTTP calls to Supabase from Elm:

Here’s what a HTTP request looks like in Elm:

HTTP calls to Supabase require you to specify: a url, an apikey, a method, and an authorisation key if you are accessing “row-level secured” data.

To find these:

  • Go to [Your project] > API > Introduction to get the “API URL”. All the other URLs you’ll use will be variations of this. However, if you’re using the Storage APIs, you’ll need to append “/storage/v1/object/[bucket name]” to the Project URL to get it to work.
  • Go to [Your project] > API > [table name], and on the top of the rightmost column, choose “Bash”, click the “hide” button and choose “anon (public)”.
  • Scroll down to the section describing what you want to do (e.g. “Read rows”). On the right, you’ll see the URL, the method it uses (DELETE, GET, POST or PUT), the “apikey”, and the parameters the operation needs. (Note that the “authorisation key” will not be accurate because that key only gets produced once a user logs in.)
curl -X POST '<https://xyz.supabase.co/rest/v1/>[table name]' \\
-H "apikey: SUPABASE_KEY" \\
-H "Authorization: Bearer SUPABASE_KEY" \\
-H "Content-Type: application/json" \\
-H "Prefer: return=representation" \\
-d '{ "some_column": "someValue", "other_column": "otherValue" }'

Construct the HTTP call.

  • Note that you’ll pass the “apikey” (and all the stuff that starts with “-H” in the rightmost column) as a header. (Note that the “Authorization” key needs the word “Bearer “ added before the value.) For example:
-H "apikey: SUPABASE_KEY" \\
-H "Authorization: Bearer SUPABASE_KEY" \\
-H "Content-Type: application/json" \\
-H "Prefer: return=representation" \\

turns into

  • Encode all the required parameters (all the stuff that starts with “-d” in the rightmost column) as JSON, and pass them as the request’s jsonBody. (Note that sometimes lists of parameters are needed.) For example:
-d '{ "some_column": "someValue", "other_column": "otherValue" }'

turns into

  • Specify how the response will be handled.
  1. What is expected as a response? Will it be a string, some JSON, or some bytes. You can ignore whatever is sent, as well.
  2. Write a decoder to handle the response in case you are not ignoring it.
  3. Specify the format of the message that’ll be trigged once the response is decoded.
  4. Write the handler for the message in the update function.
, expect = Http.expectJson UserSignedUp signUpResponseDecoder

Pain points:

  • The biggest pain is Supabase’s lack of documentation.
  1. The format of responses are not specified anywhere. One has to use software like Postman to figure out what might be returned. I suppose Supabase, probably rightly, thinks that most of their users will use the library they’ve provided.
  2. The link to the Supabase Storage’s REST API uses is tucked away in the documentation. The API is not entirely accurate either — some URLs need a small tweak (you’ll need to append “/storage/v1/object/[bucket name]” to the URL shown) before they work, and the return types, though specified here, are not all as specified.
  3. You can mitigate the pain by using Postman or the Rested plugin to test your HTTP calls and figure out what is being returned.
  • Policies for the “object” have to be set up in Supabase before the storage can be accessed in a secure way. I spent quite some time figuring this out because you only get very obscure errors when things fail.
  • A lot of code needs to be written in Elm to handle an HTTP call. However, know that once you’re through, you’ll have a bullet-proof application on your hands. No runtime errors!

However, once I had everything up and running, it was a joy to use the web app, and a very satisfying effort.

Hope all this helped you use Supabase within your Elm app!

If you’d like, you can buy me a coffee!

--

--