Version 1.0 - Last Updated: December 28, 2024
Base URL: https://bodymode.netlify.app
Body Mode's backend is powered by Netlify serverless functions that provide a secure proxy to Google's Gemini AI API. This architecture keeps API keys server-side while providing fast, scalable AI features to the mobile app.
The API does not require client-side authentication. The Gemini API key is securely stored as an environment variable on Netlify servers and never exposed to clients.
Note: While the endpoint is publicly accessible, it implements request validation and rate limiting to prevent abuse.
/.netlify/functions/gemini-proxy
Proxies requests to Google's Gemini AI API for food recognition, plan generation, chat, and other AI features.
| Header | Value | Required |
|---|---|---|
Content-Type |
application/json |
✅ Yes |
| Parameter | Type | Required | Description |
|---|---|---|---|
model |
string | ✅ Yes | Gemini model name (e.g., "gemini-1.5-flash", "gemini-1.5-pro") |
contents |
object | ✅ Yes | Message content with parts array containing text/images |
config |
object | ❌ No | Optional configuration (systemInstruction, responseMimeType, etc.) |
{
"model": "gemini-1.5-flash",
"contents": {
"parts": [
{
"text": "Analyze this meal and provide nutritional information"
},
{
"inlineData": {
"mimeType": "image/jpeg",
"data": "base64_encoded_image_data..."
}
}
]
},
"config": {
"systemInstruction": "You are a nutrition expert...",
"responseMimeType": "application/json",
"generationConfig": {
"temperature": 0.7,
"maxOutputTokens": 2048
}
}
}
{
"candidates": [
{
"content": {
"parts": [
{
"text": "{\"food\":\"Chicken Salad\",\"calories\":350,...}"
}
],
"role": "model"
},
"finishReason": "STOP"
}
],
"text": "{\"food\":\"Chicken Salad\",\"calories\":350,...}"
}
| Status Code | Reason | Response Example |
|---|---|---|
400 |
Bad Request | {"error": "Missing required fields"} |
405 |
Method Not Allowed | {"error": "Method not allowed"} |
413 |
Payload Too Large | {"error": "Request too large"} |
500 |
Server Error | {"error": "Internal server error"} |
503 |
Service Unavailable | {"error": "Gemini API unavailable"} |
Our free tier provides generous limits for all users:
These limits are shared across all users. If you experience rate limiting, please wait a few seconds and retry.
All requests are validated before being forwarded to Gemini AI:
POST /.netlify/functions/gemini-proxy
{
"model": "gemini-1.5-flash",
"contents": {
"parts": [
{"text": "Analyze this food image"},
{"inlineData": {"mimeType": "image/jpeg", "data": "..."}}
]
},
"config": {
"systemInstruction": "You are a nutrition expert...",
"responseMimeType": "application/json"
}
}
POST /.netlify/functions/gemini-proxy
{
"model": "gemini-1.5-flash",
"contents": {
"parts": [
{"text": "Generate a personalized daily plan for..."}
]
},
"config": {
"systemInstruction": "Create personalized health plans...",
"responseMimeType": "application/json"
}
}
POST /.netlify/functions/gemini-proxy
{
"model": "gemini-1.5-flash",
"contents": {
"parts": [
{"text": "User: How many calories should I eat?\nAI: "}
]
},
"config": {
"systemInstruction": "You are a health and fitness coach...",
"generationConfig": {"temperature": 0.9}
}
}
When integrating with the API, implement robust error handling:
async function callGeminiProxy(data) {
try {
const response = await fetch(
'https://bodymode.netlify.app/.netlify/functions/gemini-proxy',
{
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(data),
signal: AbortSignal.timeout(45000) // 45s timeout
}
);
if (!response.ok) {
const error = await response.json();
throw new Error(error.error || 'API request failed');
}
return await response.json();
} catch (error) {
if (error.name === 'TimeoutError') {
console.error('Request timeout - try again');
} else if (error.name === 'NetworkError') {
console.error('Network error - check connection');
} else {
console.error('API Error:', error.message);
}
throw error;
}
}
The Body Mode React Native app uses a dedicated service wrapper to call the API:
mobile/src/services/netlifyGeminiService.ts
curl -X POST https://bodymode.netlify.app/.netlify/functions/gemini-proxy \
-H "Content-Type: application/json" \
-d '{
"model": "gemini-1.5-flash",
"contents": {
"parts": [{"text": "Say hello"}]
}
}'
https://bodymode.netlify.app/.netlify/functions/gemini-proxyContent-Type: application/jsonfetch('https://bodymode.netlify.app/.netlify/functions/gemini-proxy', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
model: 'gemini-1.5-flash',
contents: {parts: [{text: 'Hello, AI!'}]}
})
})
.then(res => res.json())
.then(data => console.log(data))
.catch(err => console.error(err));
If you encounter issues with the API or have questions: