Skip to main content
RapidDev - Software Development Agency
supabase-tutorial

How to Call an Edge Function from Frontend in Supabase

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.

What you'll learn

  • How to invoke Edge Functions using supabase.functions.invoke()
  • How to pass request bodies, custom headers, and auth tokens
  • How to handle responses, errors, and different content types
  • How to configure CORS in the Edge Function for browser requests
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Advanced8 min read10-15 minSupabase (all plans), @supabase/supabase-js v2+, any frontend frameworkMarch 2026RapidDev Engineering Team
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.

Prerequisites

  • 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.

typescript
1import { createClient } from '@supabase/supabase-js'
2
3const supabase = createClient(
4 'https://your-project.supabase.co',
5 'your-anon-key'
6)
7
8// Basic invocation with a JSON body
9const { data, error } = await supabase.functions.invoke('hello-world', {
10 body: { name: 'Alice' },
11})
12
13if (error) {
14 console.error('Function error:', error.message)
15} else {
16 console.log('Response:', data) // { message: 'Hello Alice!' }
17}

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.

typescript
1const { data, error } = await supabase.functions.invoke('process-payment', {
2 body: {
3 amount: 2999,
4 currency: 'usd',
5 },
6 headers: {
7 'x-idempotency-key': 'unique-request-id-123',
8 'x-custom-header': 'my-value',
9 },
10})
11
12if (error) {
13 console.error('Payment error:', error.message)
14} else {
15 console.log('Payment result:', data)
16}

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.

typescript
1// JSON response (default behavior)
2const { data: jsonData } = await supabase.functions.invoke('get-report', {
3 body: { month: 'march' },
4})
5
6// For non-JSON responses, check the error
7// The Supabase client may return an error if JSON parsing fails
8// In that case, use fetch() directly for binary/text responses
9
10// Alternative: Raw fetch for file downloads
11const response = await fetch(
12 'https://your-project.supabase.co/functions/v1/generate-pdf',
13 {
14 method: 'POST',
15 headers: {
16 Authorization: `Bearer ${(await supabase.auth.getSession()).data.session?.access_token}`,
17 apikey: 'your-anon-key',
18 'Content-Type': 'application/json',
19 },
20 body: JSON.stringify({ reportId: 42 }),
21 }
22)
23
24const blob = await response.blob()
25const 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.

typescript
1// supabase/functions/hello-world/index.ts
2const corsHeaders = {
3 'Access-Control-Allow-Origin': '*',
4 'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',
5 'Access-Control-Allow-Methods': 'POST, GET, OPTIONS, PUT, DELETE',
6}
7
8Deno.serve(async (req) => {
9 // CRITICAL: Handle OPTIONS preflight
10 if (req.method === 'OPTIONS') {
11 return new Response('ok', { headers: corsHeaders })
12 }
13
14 try {
15 const { name } = await req.json()
16 return new Response(
17 JSON.stringify({ message: `Hello ${name}!` }),
18 { headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
19 )
20 } catch (err) {
21 // CORS headers in error responses too
22 return new Response(
23 JSON.stringify({ error: 'Internal error' }),
24 { status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
25 )
26 }
27})

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.

typescript
1async function callEdgeFunction(payload: Record<string, unknown>) {
2 try {
3 const { data, error } = await supabase.functions.invoke('my-function', {
4 body: payload,
5 })
6
7 if (error) {
8 // Error from the Edge Function (non-2xx status)
9 console.error('Function returned error:', error.message)
10
11 // Check if it's an auth error
12 if (error.message.includes('Unauthorized') || error.message.includes('401')) {
13 // Redirect to login or refresh session
14 await supabase.auth.refreshSession()
15 // Retry the call
16 return callEdgeFunction(payload)
17 }
18
19 throw error
20 }
21
22 return data
23 } catch (err) {
24 // Network error or unexpected failure
25 console.error('Failed to invoke function:', err)
26 throw err
27 }
28}

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

Complete working example

invoke-edge-function.ts
1// Complete example: Calling a Supabase Edge Function from the frontend
2import { createClient } from '@supabase/supabase-js'
3
4const supabase = createClient(
5 process.env.NEXT_PUBLIC_SUPABASE_URL!,
6 process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
7)
8
9// Type-safe function invocation helper
10interface FunctionPayload {
11 action: string
12 data: Record<string, unknown>
13}
14
15interface FunctionResponse {
16 success: boolean
17 result: unknown
18 error?: string
19}
20
21async function invokeFunction(
22 functionName: string,
23 payload: FunctionPayload
24): Promise<FunctionResponse> {
25 const { data, error } = await supabase.functions.invoke<FunctionResponse>(
26 functionName,
27 {
28 body: payload,
29 headers: {
30 'x-request-id': crypto.randomUUID(),
31 },
32 }
33 )
34
35 if (error) {
36 console.error(`Edge Function '${functionName}' error:`, error.message)
37 return { success: false, result: null, error: error.message }
38 }
39
40 return data
41}
42
43// Usage examples:
44
45// 1. Simple invocation
46const greeting = await invokeFunction('hello-world', {
47 action: 'greet',
48 data: { name: 'Alice' },
49})
50
51// 2. Authenticated invocation (token sent automatically)
52const profile = await invokeFunction('get-profile', {
53 action: 'fetch',
54 data: { includeStats: true },
55})
56
57// 3. Error handling
58const payment = await invokeFunction('process-payment', {
59 action: 'charge',
60 data: { amount: 2999, currency: 'usd' },
61})
62
63if (!payment.success) {
64 console.error('Payment failed:', payment.error)
65}

Common mistakes when calling an Edge Function from Frontend in Supabase

Why it's a problem: Not handling the OPTIONS preflight request in the Edge Function, causing CORS errors in the browser

How to avoid: 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.

Why it's a problem: Manually setting the Authorization header in functions.invoke(), overriding the automatic auth token

How to avoid: Let the Supabase client handle the Authorization and apikey headers automatically. Only pass custom headers that your function specifically needs.

Why it's a problem: Assuming functions.invoke() will work for binary responses like file downloads

How to avoid: Use raw fetch() for non-JSON responses (files, streams, images). The invoke method is designed for JSON request-response patterns.

Why it's a problem: Not checking the error property and using data directly, which may be null

How to avoid: 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

Still stuck?

Copy one of these prompts to get a personalized, step-by-step explanation.

ChatGPT Prompt

Show me how to call a Supabase Edge Function from a React component using supabase.functions.invoke(). The function processes an order and returns a confirmation. Include TypeScript types, error handling, loading state, and a retry mechanism for failed requests.

Supabase Prompt

Create a frontend utility module that wraps supabase.functions.invoke() with TypeScript generics, automatic retry on 5xx errors, request ID tracking, and structured error handling. Include example usage for calling an order processing Edge Function.

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.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.