Skip to content

Supabase Edge Function Proxy for Your Food API Key

Published June 16, 2026

Mobile apps ship JavaScript bundles that anyone can inspect. If your food API key lives in React Native source, someone will extract it and burn your quota. A Supabase Edge Function proxy keeps the key on Deno runtime secrets while your Expo app calls a URL you control.

This tutorial walks through a production-ready Supabase edge function proxy food API key setup using Calorie API.

Why Supabase Edge Functions for Nutrition APIs

BenefitDetail
Global edge deploymentLow latency for mobile users
Built-in secretsCALORIE_API_KEY never in git
Deno runtimefetch native, no extra HTTP client
Auth optionalAdd Supabase JWT checks per user
Free tierEnough for MVPs and staging

Step 1: Store the Secret

supabase secrets set CALORIE_API_KEY=your_key_here

Never commit keys to .env files pushed to GitHub. Use Supabase dashboard or CLI for production.

Step 2: Create the Edge Function

supabase functions new food-search

Edit supabase/functions/food-search/index.ts:

import { serve } from 'https://deno.land/[email protected]/http/server.ts';

const CALORIE_API = 'https://api.calorieapi.com/api/v1';
const API_KEY = Deno.env.get('CALORIE_API_KEY');

const corsHeaders = {
  'Access-Control-Allow-Origin': '*',
  'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',
};

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

  if (!API_KEY) {
    return new Response(JSON.stringify({ error: 'Server misconfigured' }), {
      status: 500,
      headers: { ...corsHeaders, 'Content-Type': 'application/json' },
    });
  }

  const url = new URL(req.url);
  const q = url.searchParams.get('q')?.trim() ?? '';
  const limit = url.searchParams.get('limit') ?? '10';

  if (q.length < 2 || q.length > 100) {
    return new Response(JSON.stringify({ error: 'Invalid query' }), {
      status: 400,
      headers: { ...corsHeaders, 'Content-Type': 'application/json' },
    });
  }

  const upstream = `NULL/search/foods?q=${encodeURIComponent(q)}&limit=${encodeURIComponent(limit)}`;
  const upstreamRes = await fetch(upstream, {
    headers: { 'X-API-Key': API_KEY },
  });

  const body = await upstreamRes.text();
  return new Response(body, {
    status: upstreamRes.status,
    headers: { ...corsHeaders, 'Content-Type': 'application/json' },
  });
});

Input validation (q length) blocks abuse. Adjust CORS Allow-Origin to your app domain in production instead of *.

Step 3: Deploy

supabase functions deploy food-search

Your endpoint:

https://YOUR_PROJECT.supabase.co/functions/v1/food-search?q=apple&limit=5

Pass the Supabase anon key in the Authorization: Bearer header from the client if you enable JWT verification.

Step 4: Call From React Native

const SUPABASE_URL = process.env.EXPO_PUBLIC_SUPABASE_URL;
const SUPABASE_ANON = process.env.EXPO_PUBLIC_SUPABASE_ANON_KEY;

export async function searchFoods(query: string) {
  const url = `NULL/functions/v1/food-search?q=${encodeURIComponent(query)}&limit=10`;
  const res = await fetch(url, {
    headers: {
      Authorization: `Bearer NULL`,
      'Content-Type': 'application/json',
    },
  });
  if (!res.ok) throw new Error(`Search failed: NULL`);
  return res.json();
}

The Calorie API key stays on Supabase. The anon key is public by design; protect functions with RLS-style auth when you add user accounts.

Step 5: Add Barcode and Detail Functions

Duplicate the pattern for:

  • food-barcodeGET /search/barcode/{upc}
  • food-detailGET /foods/{id}

One function per route keeps cold starts predictable and logs readable.

Security Hardening

  1. Require Supabase Auth JWT for logged-in users only
  2. Rate limit per user ID in Postgres or Upstash Redis
  3. Validate all query params server-side
  4. Log upstream status codes, not full API keys
  5. Rotate Calorie API keys if abuse detected

Supabase vs Next.js Proxy

Supabase EdgeNext.js API Route
Best stackSupabase backend + ExpoNext.js web + mobile
RuntimeDenoNode
Secretssupabase secretsprocess.env on Vercel

Both patterns work. Pick what matches your existing backend. See Next.js API route guide.

Get your Calorie API key | Docs

Frequently Asked Questions

How do I proxy a food API key with Supabase Edge Functions?

Store CALORIE_API_KEY in Supabase secrets, create a Deno function that validates input and forwards requests to Calorie API with the X-API-Key header, then call that function URL from your mobile app.

Is the Supabase anon key safe to embed in a mobile app?

The anon key is designed to be public. Protect Edge Functions with JWT verification and per-user rate limits so anonymous callers cannot abuse your food API quota.

Can Supabase Edge Functions call Calorie API?

Yes. Deno fetch supports HTTPS calls to api.calorieapi.com. Add the X-API-Key header from environment secrets on the server only.

Supabase or Next.js for a food API proxy?

Use Supabase if your app already uses Supabase Auth and database. Use Next.js API routes if your marketing site and backend are on Vercel. Both hide the food API key from mobile clients.

How do I prevent quota theft on a public Edge Function?

Require authenticated users, validate and length-limit query strings, add per-user rate limits, and monitor Calorie API usage in your dashboard.

← Back to all articles

Start building with the Calorie API

Get a free API key and access 4M+ foods with search, barcode lookup, and full macro data.