# How to Call an Edge Function from Frontend in Supabase

- Tool: Supabase
- Difficulty: Advanced
- Time required: 10-15 min
- Compatibility: Supabase (all plans), @supabase/supabase-js v2+, any frontend framework
- Last updated: March 2026

## TL;DR

To call a Supabase Edge Function from the frontend, use supabase.functions.invoke('function-name', { body: { ... } }). The client automatically sends the user's auth token and the project's anon key. Make sure your Edge Function handles CORS with an OPTIONS preflight response. You can also call Edge Functions with a raw fetch request if you need more control over headers and request configuration.

## Calling Supabase Edge Functions from Your Frontend Application

Supabase Edge Functions run server-side TypeScript code, but you invoke them from the browser like any API call. The Supabase JS client provides a functions.invoke() method that automatically handles authentication headers, making it the easiest way to call your Edge Functions. This tutorial covers the invoke method, handling different response types, error handling, CORS configuration, and using raw fetch as an alternative.

## Before you start

- A deployed Supabase Edge Function (see how-to-use-supabase-edge-functions)
- @supabase/supabase-js installed in your frontend project
- Supabase client initialized with your project URL and anon key
- Basic understanding of async/await and HTTP requests

## Step-by-step guide

### 1. Invoke an Edge Function with the Supabase client

The simplest way to call an Edge Function is with supabase.functions.invoke(). Pass the function name as the first argument and an options object with a body property. The client automatically adds the Authorization header with the logged-in user's JWT token and the apikey header with your project's anon key. The response is parsed as JSON by default.

```
import { createClient } from '@supabase/supabase-js'

const supabase = createClient(
  'https://your-project.supabase.co',
  'your-anon-key'
)

// Basic invocation with a JSON body
const { data, error } = await supabase.functions.invoke('hello-world', {
  body: { name: 'Alice' },
})

if (error) {
  console.error('Function error:', error.message)
} else {
  console.log('Response:', data) // { message: 'Hello Alice!' }
}
```

**Expected result:** The Edge Function is called and returns a JSON response that is automatically parsed.

### 2. Pass custom headers to the Edge Function

You can send additional headers alongside the automatic auth headers. This is useful for passing API keys, content type overrides, or custom metadata that your Edge Function needs. Custom headers are merged with the default headers — they do not replace them.

```
const { data, error } = await supabase.functions.invoke('process-payment', {
  body: {
    amount: 2999,
    currency: 'usd',
  },
  headers: {
    'x-idempotency-key': 'unique-request-id-123',
    'x-custom-header': 'my-value',
  },
})

if (error) {
  console.error('Payment error:', error.message)
} else {
  console.log('Payment result:', data)
}
```

**Expected result:** The Edge Function receives both the automatic auth headers and your custom headers.

### 3. Handle different response types

By default, functions.invoke() tries to parse the response as JSON. If your Edge Function returns a different content type (like a file or plain text), you need to handle it appropriately. Check the response content type or use the raw Response object for non-JSON responses.

```
// JSON response (default behavior)
const { data: jsonData } = await supabase.functions.invoke('get-report', {
  body: { month: 'march' },
})

// For non-JSON responses, check the error
// The Supabase client may return an error if JSON parsing fails
// In that case, use fetch() directly for binary/text responses

// Alternative: Raw fetch for file downloads
const response = await fetch(
  'https://your-project.supabase.co/functions/v1/generate-pdf',
  {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${(await supabase.auth.getSession()).data.session?.access_token}`,
      apikey: 'your-anon-key',
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ reportId: 42 }),
  }
)

const blob = await response.blob()
const url = URL.createObjectURL(blob)
```

**Expected result:** JSON responses are parsed automatically; non-JSON responses are handled with raw fetch.

### 4. Configure CORS in your Edge Function for browser requests

Browser requests to Edge Functions require CORS headers. Unlike the Supabase REST API (which handles CORS automatically), Edge Functions need manual CORS configuration. Your function must handle OPTIONS preflight requests and include CORS headers in every response, including error responses. Without this, browsers will block the request with a CORS error.

```
// supabase/functions/hello-world/index.ts
const corsHeaders = {
  'Access-Control-Allow-Origin': '*',
  'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',
  'Access-Control-Allow-Methods': 'POST, GET, OPTIONS, PUT, DELETE',
}

Deno.serve(async (req) => {
  // CRITICAL: Handle OPTIONS preflight
  if (req.method === 'OPTIONS') {
    return new Response('ok', { headers: corsHeaders })
  }

  try {
    const { name } = await req.json()
    return new Response(
      JSON.stringify({ message: `Hello ${name}!` }),
      { headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
    )
  } catch (err) {
    // CORS headers in error responses too
    return new Response(
      JSON.stringify({ error: 'Internal error' }),
      { status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
    )
  }
})
```

**Expected result:** Browser requests work without CORS errors because all responses include proper headers.

### 5. Handle errors and edge cases

The functions.invoke() method returns an error object when the function returns a non-2xx status code or when the request fails entirely (network error, timeout). Always check for errors before using the data. For more detailed error handling, check the error's context property which may contain the HTTP status code.

```
async function callEdgeFunction(payload: Record<string, unknown>) {
  try {
    const { data, error } = await supabase.functions.invoke('my-function', {
      body: payload,
    })

    if (error) {
      // Error from the Edge Function (non-2xx status)
      console.error('Function returned error:', error.message)

      // Check if it's an auth error
      if (error.message.includes('Unauthorized') || error.message.includes('401')) {
        // Redirect to login or refresh session
        await supabase.auth.refreshSession()
        // Retry the call
        return callEdgeFunction(payload)
      }

      throw error
    }

    return data
  } catch (err) {
    // Network error or unexpected failure
    console.error('Failed to invoke function:', err)
    throw err
  }
}
```

**Expected result:** Errors are caught and handled gracefully, with auth errors triggering a session refresh.

## Complete code example

File: `invoke-edge-function.ts`

```typescript
// Complete example: Calling a Supabase Edge Function from the frontend
import { createClient } from '@supabase/supabase-js'

const supabase = createClient(
  process.env.NEXT_PUBLIC_SUPABASE_URL!,
  process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
)

// Type-safe function invocation helper
interface FunctionPayload {
  action: string
  data: Record<string, unknown>
}

interface FunctionResponse {
  success: boolean
  result: unknown
  error?: string
}

async function invokeFunction(
  functionName: string,
  payload: FunctionPayload
): Promise<FunctionResponse> {
  const { data, error } = await supabase.functions.invoke<FunctionResponse>(
    functionName,
    {
      body: payload,
      headers: {
        'x-request-id': crypto.randomUUID(),
      },
    }
  )

  if (error) {
    console.error(`Edge Function '${functionName}' error:`, error.message)
    return { success: false, result: null, error: error.message }
  }

  return data
}

// Usage examples:

// 1. Simple invocation
const greeting = await invokeFunction('hello-world', {
  action: 'greet',
  data: { name: 'Alice' },
})

// 2. Authenticated invocation (token sent automatically)
const profile = await invokeFunction('get-profile', {
  action: 'fetch',
  data: { includeStats: true },
})

// 3. Error handling
const payment = await invokeFunction('process-payment', {
  action: 'charge',
  data: { amount: 2999, currency: 'usd' },
})

if (!payment.success) {
  console.error('Payment failed:', payment.error)
}
```

## Common mistakes

- **Not handling the OPTIONS preflight request in the Edge Function, causing CORS errors in the browser** — undefined Fix: Add an OPTIONS handler at the top of your Edge Function that returns a 200 with CORS headers. Every browser sends a preflight OPTIONS request before the actual request.
- **Manually setting the Authorization header in functions.invoke(), overriding the automatic auth token** — undefined Fix: Let the Supabase client handle the Authorization and apikey headers automatically. Only pass custom headers that your function specifically needs.
- **Assuming functions.invoke() will work for binary responses like file downloads** — undefined Fix: Use raw fetch() for non-JSON responses (files, streams, images). The invoke method is designed for JSON request-response patterns.
- **Not checking the error property and using data directly, which may be null** — undefined Fix: Always check for errors first: if (error) { handle error } else { use data }. When the function returns a non-2xx status, data may be null.

## Best practices

- Use supabase.functions.invoke() for JSON-based Edge Function calls — it handles auth headers automatically
- Always handle both success and error cases when invoking Edge Functions
- Create a wrapper function with TypeScript generics for type-safe Edge Function calls
- Use raw fetch() for non-JSON responses like file downloads or streaming
- Include CORS headers in every Edge Function response, including error responses
- Add request IDs via custom headers for debugging and tracing in production
- Implement retry logic with exponential backoff for transient failures
- Keep Edge Function payloads small — pass IDs and let the function fetch data from the database

## Frequently asked questions

### Does functions.invoke() automatically send the user's auth token?

Yes. If the user is logged in, the Supabase client automatically includes their JWT in the Authorization header. If not logged in, only the anon key is sent via the apikey header.

### Can I call an Edge Function without authentication?

Yes. Deploy the function with --no-verify-jwt (supabase functions deploy my-function --no-verify-jwt). This allows unauthenticated requests, which is necessary for public webhooks or unauthenticated APIs.

### Why do I get a CORS error even though the function works with curl?

Curl does not enforce CORS — only browsers do. Your Edge Function must handle the OPTIONS preflight request and include Access-Control-Allow-Origin headers in every response. Check that error responses also include CORS headers.

### What happens if the Edge Function times out?

Edge Functions have a 150-second timeout. If exceeded, the client receives a timeout error. For long-running tasks, return a task ID immediately and have the client poll a database table for the result.

### Can I invoke Edge Functions from server-side code (not a browser)?

Yes. Use the same supabase.functions.invoke() method or a raw HTTP request. Server-side calls do not need CORS handling. You can also use the service role key for admin-level access.

### How do I test Edge Function calls locally?

Run supabase functions serve to start the local function server. Point your frontend's Supabase URL to http://localhost:54321 and the functions will be available at http://localhost:54321/functions/v1/<name>.

### Can RapidDev help build Edge Function integrations for my app?

Yes. RapidDev can architect Edge Function APIs, implement CORS handling, build type-safe client wrappers, and set up error handling patterns for your Supabase Edge Function integrations.

---

Source: https://www.rapidevelopers.com/supabase-tutorial/how-to-call-an-edge-function-from-frontend-in-supabase
© RapidDev — https://www.rapidevelopers.com/supabase-tutorial/how-to-call-an-edge-function-from-frontend-in-supabase
