Supabase Functions

  • Supabase has a concept of functions. These are code written in typescript, meant for deno code that gets deployed by supabase on their edge locations.
  • Each "function" is a standalone application, with it's own deployment, configuration, logs etc.
  • you create a new "function" using supabase functions new the-name
  • this command creates a folder named supabase/functions/the-name, with a file containing index.ts which looks like this:
import { serve } from "https://deno.land/std@0.168.0/http/server.ts"

console.log("Hello from Functions!")

serve(async (req) => {
  const { name } = await req.json()
  const data = {
    message: `Hello ${name}!`,
  }

  return new Response(
    JSON.stringify(data),
    { headers: { "Content-Type": "application/json" } },
  )
})
  • to test the function locally you have to start supabase using supabase start and then run functions, supabase functions serve
  • to call the function locally you have to do this:
curl --request POST 'http://localhost:54321/functions/v1/the-name' \n  --header 'Authorization: Bearer <anon key>' \n  --header 'Content-Type: application/json' \n  --data '{ "name": "Functions" }'
  • on local the anon key header is optional
  • functions are deployed on multiple servers all around the world and supposedly have low latency benefits.
  • on each location, probably one instance of the "function server" runs, they auto restart the server when needed.
  • function server can be thought as a standlone server, written in deno, running somewhere.
  • the "Hello from Functions!" is printed when the "function server" starts.
  • the server probably starts on demand as there are many locations and there is no need to keep all of them running. In the logs I only saw one "hello from functions!" printed, so it probably started one even when there was no request, maybe to assure me, that it is indeed deployed.
  • the function passed to serve() is called on every function request.
connecting to db from function
import * as postgres from 'https://deno.land/x/postgres@v0.14.2/mod.ts'
import { serve } from 'https://deno.land/std@0.177.0/http/server.ts'

// Get the connection string from the environment variable "SUPABASE_DB_URL"
const databaseUrl = Deno.env.get('SUPABASE_DB_URL')!

// Create a database pool with three connections that are lazily established
const pool = new postgres.Pool(databaseUrl, 3, true)

serve(async (_req) => {
  try {
    // Grab a connection from the pool
    const connection = await pool.connect()

    try {
      // Run a query
      const result = await connection.queryObject`SELECT * FROM animals`
      const animals = result.rows // [{ id: 1, name: "Lion" }, ...]
      console.log(animals)

      // Encode the result as pretty printed JSON
      const body = JSON.stringify(
        animals,
        (key, value) => (typeof value === 'bigint' ? value.toString() : value),
        2
      )

      // Return the response with the correct content type header
      return new Response(body, {
        status: 200,
        headers: {
          'Content-Type': 'application/json; charset=utf-8',
        },
      })
    } finally {
      // Release the connection back into the pool
      connection.release()
    }
  } catch (err) {
    console.error(err)
    return new Response(String(err?.message ?? err), { status: 500 })
  }
})
Template strings for safe query arguments
const min = 10;
const max = 20;
const result = await connection
  .queryObject`SELECT ID, NAME FROM PEOPLE WHERE AGE > ${min} AND AGE < ${max}`;
console.log(result.rows);
  • connection.queryObjectSELECT * FROM animals`