RORK LABJP
TOOLING — Rork's developer repos keep moving: rork-xcode was updated on July 16, rork-device on July 15, and rork-plist on July 13OPUS46 — Claude Opus 4.6 is live in Rork, and Rork Max is built to assemble apps on top of Claude CodeSIM — A cloud iOS simulator runs in the browser, with one click to install on a device and two clicks to publish to the App StoreMAX — Rork Max emits pure Swift rather than React Native, reaching iPhone, iPad, Apple Watch, Apple TV, Vision Pro, and even iMessageNATIVE — That opens up HealthKit, ARKit and LiDAR, NFC, Dynamic Island, Live Activities, 3D through Metal, and on-device inference with Core MLSEED — Rork raised a $15M seed led by Left Lane Capital, with Peak XV and a16z Speedrun joining the roundTOOLING — Rork's developer repos keep moving: rork-xcode was updated on July 16, rork-device on July 15, and rork-plist on July 13OPUS46 — Claude Opus 4.6 is live in Rork, and Rork Max is built to assemble apps on top of Claude CodeSIM — A cloud iOS simulator runs in the browser, with one click to install on a device and two clicks to publish to the App StoreMAX — Rork Max emits pure Swift rather than React Native, reaching iPhone, iPad, Apple Watch, Apple TV, Vision Pro, and even iMessageNATIVE — That opens up HealthKit, ARKit and LiDAR, NFC, Dynamic Island, Live Activities, 3D through Metal, and on-device inference with Core MLSEED — Rork raised a $15M seed led by Left Lane Capital, with Peak XV and a16z Speedrun joining the round
Articles/Dev Tools
Dev Tools/2026-05-11Intermediate

That 'CORS Error' in Your Rork + Supabase Edge Function? It's Probably Something Else

Seeing CORS errors when calling Supabase Edge Functions from your Rork app? React Native apps don't enforce CORS — the real cause is usually something else entirely. Here's how to diagnose and fix it.

Supabase33Edge Functions2CORS2troubleshooting65React Native209

Every time someone tells me they're getting Access-Control-Allow-Origin errors when calling Supabase Edge Functions from their Rork app, the first thing I do is ask them to check the actual error logs. More often than not, CORS isn't the real problem at all.

I've been building mobile apps since 2014, and "CORS error" is one of those terms that gets used for almost any API call failure. When something doesn't work across a network boundary, CORS gets the blame. The Rork + Supabase combination triggers this exact misdiagnosis regularly.

React Native Doesn't Enforce CORS — That's the Key Insight

CORS (Cross-Origin Resource Sharing) is a browser security mechanism. When a page running on https://example.com makes a fetch request to https://api.another.com, the browser blocks it unless the server explicitly allows it. This prevents malicious sites from silently sending requests to other services on your behalf.

React Native — the framework powering Rork apps — runs in a native iOS or Android runtime, not a browser. Which means CORS restrictions simply don't apply when your app is running on a real device or in TestFlight.

// This fetch has no CORS restrictions in React Native
const { data, error } = await supabase.functions.invoke('my-function', {
  body: { message: 'hello' }
})

So why do developers see CORS-like symptoms? There are three patterns worth knowing.

Pattern 1 — You're Testing in Rork Companion (Browser Preview)

When you preview your Rork app in the PC browser via Companion, that preview runs in a genuine browser environment. And in that environment, CORS absolutely applies.

If the error only shows up in browser preview but not on a real device, the fix is adding CORS headers to your Edge Function:

// supabase/functions/my-function/index.ts
 
const corsHeaders = {
  'Access-Control-Allow-Origin': '*',
  'Access-Control-Allow-Headers':
    'authorization, x-client-info, apikey, content-type',
}
 
Deno.serve(async (req) => {
  // Handle preflight requests
  if (req.method === 'OPTIONS') {
    return new Response('ok', { headers: corsHeaders })
  }
 
  try {
    const { message } = await req.json()
    const result = { response: `Received: ${message}` }
 
    return new Response(JSON.stringify(result), {
      headers: { ...corsHeaders, 'Content-Type': 'application/json' },
    })
  } catch (error) {
    // Critical: attach corsHeaders to error responses too
    return new Response(JSON.stringify({ error: error.message }), {
      headers: { ...corsHeaders, 'Content-Type': 'application/json' },
      status: 400,
    })
  }
})

Two things matter here: responding to OPTIONS preflight requests, and attaching corsHeaders to all responses including errors. If an error response doesn't include CORS headers, the browser can't read the error body and reports a CORS error instead of the actual problem.

Pattern 2 — Your Edge Function Is Crashing with a 500 Error

This is the most frequently missed cause. When a Supabase Edge Function crashes with an unhandled exception, the Deno runtime sends back a generic error response — often without CORS headers attached. In browser preview, this looks exactly like a CORS error.

How to diagnose it:

  1. Open the Supabase dashboard → Edge Functions → Logs
  2. Find the log entry at the time of your failed request
  3. If you see Error or Uncaught, it's a function bug, not a CORS issue
// Common mistake: no try-catch
Deno.serve(async (req) => {
  const { userId } = await req.json()  // throws if body is malformed
  const user = await getUser(userId)    // throws if DB call fails
  return new Response(JSON.stringify(user))
})
 
// Fixed: wrap everything in try-catch, attach corsHeaders everywhere
Deno.serve(async (req) => {
  const corsHeaders = { 'Access-Control-Allow-Origin': '*' }
 
  if (req.method === 'OPTIONS') {
    return new Response('ok', { headers: corsHeaders })
  }
 
  try {
    const body = await req.json()
    if (!body.userId) throw new Error('userId is required')
    const user = await getUser(body.userId)
    return new Response(JSON.stringify(user), {
      headers: { ...corsHeaders, 'Content-Type': 'application/json' },
    })
  } catch (error) {
    console.error('Function error:', error.message)
    return new Response(JSON.stringify({ error: error.message }), {
      headers: { ...corsHeaders, 'Content-Type': 'application/json' },
      status: 400,
    })
  }
})

Pattern 3 — Missing Auth Headers Causing 401s

When calling Edge Functions with a raw fetch instead of supabase.functions.invoke(), missing or incorrect authorization headers result in a 401 response. If that 401 comes back without CORS headers, browser preview displays it as a CORS error.

// ❌ Missing auth headers
const response = await fetch(
  'https://xxxxx.supabase.co/functions/v1/my-function',
  {
    method: 'POST',
    body: JSON.stringify({ message: 'hello' }),
  }
)
 
// ✅ supabase-js handles headers automatically
const { data, error } = await supabase.functions.invoke('my-function', {
  body: { message: 'hello' },
})

Using supabase.functions.invoke() automatically attaches the project's ANON_KEY and the Authorization header. If you need to use raw fetch, include these manually:

const ANON_KEY = process.env.EXPO_PUBLIC_SUPABASE_ANON_KEY
 
const response = await fetch(
  'https://xxxxx.supabase.co/functions/v1/my-function',
  {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${ANON_KEY}`,
      'apikey': ANON_KEY,
    },
    body: JSON.stringify({ message: 'hello' }),
  }
)

A Practical Diagnosis Flow

Work through these steps in order and you'll identify the cause in most cases:

Step 1: Does the error appear on a real device (or iOS simulator)?

  • Error only in browser preview → Pattern 1: add CORS headers to your Edge Function
  • Error on real device too → Not a CORS problem, continue to Step 2

Step 2: Check the Supabase dashboard logs

  • Function error logs present → Pattern 2: fix the bug and wrap in try-catch
  • No logs at all → Function isn't being called (check URL and auth)

Step 3: Are you using supabase.functions.invoke()?

  • Using raw fetch → Pattern 3: switch to invoke() or add headers manually

Supabase Edge Functions are actually quite transparent about what's going wrong — the logs tell the story clearly. When "CORS error" appears, resist the urge to immediately add headers. Start with a real device test and log check instead, and you'll save yourself a lot of unnecessary configuration changes.

For deeper coverage of Edge Functions architecture, see Rork Max × Supabase Edge Functions & Row Level Security — Secure API Design Guide. For general Supabase data loading issues, Rork App: Supabase Data Not Loading — Cause-by-Cause Solutions covers the broader picture.

Share

Thank You for Reading

Rork Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

Dev Tools2026-06-23
DAU Went Up but Retention Didn't — Rebuilding Gamification That Actually Sticks in Rork Apps
Points, badges, and leaderboards lift DAU, but retention is a different story. Field notes on a server-authoritative point ledger, streaks that forgive, and leaderboards that don't crush newcomers — with working code for Rork apps.
Dev Tools2026-06-12
Your Rork App's 'Documents & Data' Keeps Growing — Taming expo-image's Disk Cache
My wallpaper app's binary was 40 MB, yet 'Documents & Data' had ballooned to 2.4 GB. Here is how I diagnosed expo-image's unbounded disk cache and fixed it with cachePolicy tuning, thumbnail URLs, and generational cache clearing.
Dev Tools2026-05-13
Supabase Storage Returns 403 After Image Upload in Rork Apps ─ Fixing RLS Policies and Bucket Settings
Uploaded an image to Supabase Storage in your Rork app, but the URL returns 403 Forbidden? This guide covers the three most common causes: bucket Public/Private settings, missing RLS SELECT policies, and the getPublicUrl vs createSignedUrl confusion — with working code examples.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →