Skip to content

How to Hide Your Nutrition API Key in a Mobile App (Backend Proxy)

Published June 17, 2026

You found the perfect nutrition API. You dropped the key in EXPO_PUBLIC_CALORIE_API_KEY. Shipped to TestFlight. Within a week, unknown IPs are hammering your endpoint. This is why you must hide your nutrition API key with a mobile app backend proxy.

This article explains the threat model, the proxy pattern, and three backends you can deploy this week.

The Problem: Mobile Binaries Are Public

PlatformRisk
iOSIPA can be unpacked; strings visible in binary
AndroidAPK/AAB decompilation exposes constants
ExpoEXPO_PUBLIC_* values baked into JS bundle
React Native HermesBytecode still contains string literals

Security through obscurity fails. Assume every user can read your client code.

What a Backend Proxy Does

Mobile app  →  YOUR server (adds secret key)  →  Calorie API
   ↑                      ↑
 no secret            CALORIE_API_KEY in env

Your app calls https://api.yourapp.com/foods/search?q=banana. Your server forwards to Calorie API with X-API-Key. Users and attackers only see your domain.

Minimum Viable Proxy Requirements

  1. Server-side secret storage (env var or vault)
  2. Input validation on query strings and IDs
  3. HTTPS only
  4. Rate limiting per IP or user
  5. Error sanitization (no stack traces to clients)
  6. Logging without printing keys

Option A: Next.js API Route

Best if you already host a Next.js site on Vercel. Full guide: secure food API key Next.js.

Option B: Supabase Edge Function

Best if you use Supabase Auth and Postgres. Full guide: Supabase edge function proxy.

Option C: Minimal Express Server

import express from 'express';
import rateLimit from 'express-rate-limit';

const app = express();
const KEY = process.env.CALORIE_API_KEY;

app.use(rateLimit({ windowMs: 60_000, max: 60 }));

app.get('/foods/search', async (req, res) => {
  const q = String(req.query.q ?? '').trim();
  if (q.length < 2 || q.length > 100) {
    return res.status(400).json({ error: 'Invalid query' });
  }
  const limit = Math.min(Number(req.query.limit) || 10, 25);
  const url = `https://api.calorieapi.com/api/v1/search/foods?q=${encodeURIComponent(q)}&limit=NULL`;
  const upstream = await fetch(url, { headers: { 'X-API-Key': KEY } });
  const data = await upstream.json();
  res.status(upstream.status).json(data);
});

app.listen(process.env.PORT || 3001);

Deploy on Railway, Fly.io, or Render. Point your mobile app at this URL.

Mobile Client: What Changes

Before (insecure):

fetch('https://api.calorieapi.com/api/v1/search/foods?q=apple', {
  headers: { 'X-API-Key': 'sk_live_exposed' },
});

After (secure):

fetch('https://api.yourapp.com/foods/search?q=apple');

Optionally attach your user session token so the proxy can rate limit per account.

Add Authentication Between App and Proxy

LayerPurpose
Firebase Auth ID tokenVerify user before forwarding
Supabase JWTSame, native to Supabase stack
Custom API tokenIssued after login to your backend

Unauthenticated public proxies are fine for demos only. Production apps should tie requests to identities.

Caching Saves Money and Latency

Cache identical search queries for 5 to 15 minutes in Redis or Cloudflare KV:

Key: search:foods:apple:10
TTL: 600 seconds

Popular foods ("banana", "egg", "rice") hit cache constantly. Your Calorie API bill drops.

Compliance and Licensing

Hiding the key also lets you:

  • Enforce commercial licensing before scaling
  • Swap providers without app store updates (change proxy target)
  • Add audit logs for enterprise wellness clients

Checklist Before App Store Submission

  • No X-API-Key string in mobile repo
  • Proxy deployed with HTTPS
  • Rate limits tested under load
  • Error messages user-friendly
  • Calorie API plan matches expected DAU

Get your free Calorie API key | Pricing

Frequently Asked Questions

Why can't I put my nutrition API key in a mobile app?

iOS and Android apps can be reverse-engineered. Any key embedded in the client can be extracted and used to consume your API quota or incur charges.

What is a backend proxy for a nutrition API?

Your mobile app calls your server, which adds the secret Calorie API key and forwards the request. The food API key never ships in the app bundle.

What is the easiest backend proxy for Expo apps?

Supabase Edge Functions and Next.js API routes are the fastest to ship. A small Express server on Railway also works if you want full control.

Should I authenticate users before proxying food API calls?

Yes for production. Tie requests to user IDs so you can rate limit abuse and monitor usage per account.

Does a proxy slow down food search?

Adds roughly 20 to 80 ms depending on region. Edge functions and caching keep search feeling instant compared to the 200 to 400 ms upstream nutrition API call.

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