Supabase Edge Functions: a working template with a catch

Hello!

If you are reading this, please Don’t miss the catch in the end.

The following working supabase edge function was inspired by the great tutorial for Creating an Address Autocomplete feature in a Search Select element in weweb, by ryanenocode here:

This Supabase edge function is called from weweb through the workflow prebuilt action: Invoke Edge Function.

It “replaces” the API call to “https://maps.googleapis.com/maps/api/place/autocomplete/” endpoint used in the tutorial above.
Takes an “input” which is the item searched for; and returns five suggestions for the Autocomplete feature.

The code is produced by GPT4o & Claude together and includes: Checking if the user making the call is logged in (Supabase Auth used), CORS, secure GOOGLE_API_KEY stored in supabase secrets and other stuff the AIs deemed necessary and works.

import { serve } from 'https://deno.land/std@0.168.0/http/server.ts';
import { corsHeaders } from '../_shared/cors.ts';
import { createClient } from 'https://cdn.jsdelivr.net/npm/@supabase/supabase-js/+esm';

const supabase = createClient(
  Deno.env.get('SUPABASE_URL')!,
  Deno.env.get('SUPABASE_ANON_KEY')!
);

serve(async (req: Request) => {
  if (req.method === 'OPTIONS') {
    return new Response(null, {
      status: 204,
      headers: {
        ...corsHeaders,
        'Access-Control-Max-Age': '86400'
      }
    });
  }

  try {
    const authHeader = req.headers.get('Authorization');
    if (!authHeader) {
      return new Response(JSON.stringify({ error: 'Missing Authorization header' }), {
        status: 401,
        headers: { ...corsHeaders, 'Content-Type': 'application/json' }
      });
    }

    const token = authHeader.replace('Bearer ', '');
    const { data, error } = await supabase.auth.getUser(token);

    if (error || !data.user) {
      return new Response(JSON.stringify({ error: 'Invalid or expired token' }), {
        status: 401,
        headers: { ...corsHeaders, 'Content-Type': 'application/json' }
      });
    }

    const url = new URL(req.url);
    const input = url.searchParams.get('input');

    console.log('Received input:', input);

    if (!input) {
      console.log('Missing input parameter');
      return new Response(JSON.stringify({ error: 'Missing input parameter' }), {
        status: 400,
        headers: { ...corsHeaders, 'Content-Type': 'application/json' }
      });
    }

    const apiKey = Deno.env.get('GOOGLE_API_KEY');
    if (!apiKey) {
      console.log('Missing API key');
      return new Response(JSON.stringify({ error: 'Missing API key' }), {
        status: 500,
        headers: { ...corsHeaders, 'Content-Type': 'application/json' }
      });
    }

    const apiUrl = new URL('https://maps.googleapis.com/maps/api/place/autocomplete/json');
    apiUrl.searchParams.append('input', input);
    apiUrl.searchParams.append('key', apiKey);

    try {
      const apiResponse = await fetch(apiUrl.toString());
      const json = await apiResponse.json();
      console.log('API response:', json);
      return new Response(JSON.stringify(json), {
        status: 200,
        headers: { ...corsHeaders, 'Content-Type': 'application/json' }
      });
    } catch (error) {
      console.error('Error fetching autocomplete suggestions', error);
      return new Response(JSON.stringify({ error: 'Error fetching autocomplete suggestions' }), {
        status: 500,
        headers: { ...corsHeaders, 'Content-Type': 'application/json' }
      });
    }
  } catch (error) {
    console.error('Unexpected error:', error);
    return new Response(JSON.stringify({ error: 'Unexpected error' }), {
      status: 500,
      headers: { ...corsHeaders, 'Content-Type': 'application/json' }
    });
  }
});

CATCH: I have no idea if this is THE secure structure to make API calls (using Supabase Auth & Edge Functions) to places for which weweb has not come up with a Plugin to make them securely for us. It is intended to be the one setup which can be used as a template with the AI’s and various API Documentations to become the go-to tool for the goal.

I kindly ask the community to use this as a working template and come up with the best structure that: even if misses being the overall secure way to call API’s from weweb, which as a scope might leave the confines of this forum, at least become something that exhausts the limits of security weweb can provide as the “initiating party”.

Of course, there is room for Google/recipient call side additions to security such as: api key restrictions, website restrictions etc.
The catch in this case becomes: are all the other forms of securing the process Welcomed Additions OR Imperatives ?

Thank you!

EDIT: my Supabase discord post regarding the same issue so maybe we can work together on this:

EDIT2: Reference of conversations here about the issue for our Supabase visitors who want to get some context

I think this should be alright, you’re validating if the user exists and is providing a valid key in the middle, so that’s great. If you want only authenticated users to be able to invoke this.

1 Like

So we have a working template for non - plugin API calls securely - if someone uses Supabase Auth and Integration ?

@Broberto My first response on whether I only want logged in users to call the function is of course… am I missing something here? I mean, the hard part is closing access, allowing everyone (per case) is the easy part, right ?

Calling @Joyce if you could please take a look at the discord link bcs some weweb details are asked and I don’t know what to say …

Discord:
“Just to reiterate: you’re passing in the user’s access token via a header; creating a Supabase client with Deno environment variables; calling getUser with the access token passed in, to verify the authenticity of the access token and user.”

@Alexis A moment of your time please sir :slight_smile:
@Max @Raphael Thoughts?

I don’t understand why people are not excited about this. It’s like a little zapier inside weweb and supabase… Only much cheaper and more flexible

Great resource: here from Supabase team member with code snippets for Edge Functions to implement:

1 Like