Supabase edge function running twice, but just one call

638 Views Asked by At

I have a relatively simple edge function in typescript at Supabase that connects to our postgres db and runs a query. The function is invoked via an html button. When run locally in the cli, or via production, once deployed, it both cases, the edge function runs twice. The query is running twice, but console logging shows the entire function is running twice. However, in the browser console logging only shows one call to the function on the button click. Any ideas?

Here's the html:

<html>
  <head>
    <meta charset="utf-8">
    <title>Invoke Supabase Function</title>
  </head>
  <body>
    <button id="invoke-button">Invoke Function</button>
    <script language="Javascript">
      const apiKey = XXXXX
      const functionEndPoint = 'http://localhost:54321/functions/v1/';

      const inputData = { name: 'Blah' };
      
      const invokeFunction = () => {
        console.log('Called invokeFunction');
        fetch(`${functionEndPoint}`, {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
            'Authorization': `Bearer ${apiKey}`
          },
          body: JSON.stringify(inputData)
        })
          .then(response => response.json())
          .then(data => {            
            console.log(data);
          })
          .catch(error => {
            console.error(error);
          });
      };
      
      document.getElementById('invoke-button').addEventListener('click', invokeFunction);
    </script>
  </body>
</html>

Here's the function:

import * as postgres from 'https://deno.land/x/[email protected]/mod.ts'
import { serve } from 'https://deno.land/[email protected]/http/server.ts'
import { createClient } from 'https://esm.sh/@supabase/[email protected]'
import { corsHeaders } from '../_shared/cors.ts'

const databaseUrl = "postgresql://XXXXX"
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 (its actually a non-deterministic fb function/stored proc)
      console.log("inbound startgame request...")
      const result = await connection.queryObject`SELECT public."newGame"();`
      const rr = result.rows
      console.log(rr)

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

      // Return the response with the correct content type header
      return new Response(
        JSON.stringify(rr),
        { 
          headers: { ...corsHeaders, "Content-Type": "application/json" },
          status: 200,
        },
      )

    } finally {
      // Release the connection back into the pool
      connection.release()
    }
  } catch (err) {
    console.error(err)
    return new Response(String(err?.message ?? err), { status: 500 })
  }
1

There are 1 best solutions below

0
On

Eureka! The edge function needs this:

  if (req.method === 'OPTIONS') {
    return new Response('ok', { headers: corsHeaders })
  }

Apparently browsers send a pre-flight request to the function to see if it supports CORs. If you don't handle this properly, the listening function just runs normally during the pre-flight check, and then again during the main call. Since the code above was returning CORs headers, both runs were successful from the browser.