Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/AndrewwCO/Panahashi/llms.txt

Use this file to discover all available pages before exploring further.

services/api.js is the single source of truth for all backend communication in Panahashi. Every screen and context that needs data goes through this file — there are no inline fetch calls elsewhere in the app. This centralizes base URL configuration, authentication headers, and error handling in one place, keeping screen code clean and consistent.

Base URL

The backend base URL is exported as BASE_URL and must be updated when switching environments:
// Android emulator
export const BASE_URL = 'http://10.0.2.2:8080/api/v1';
// iOS Simulator / web: 'http://127.0.0.1:8080/api/v1'
// Production: 'https://your-domain.com/api/v1'
Change BASE_URL to point at the correct host before building for a new environment. Android emulators route 10.0.2.2 to the host machine’s localhost; iOS simulators and the web use 127.0.0.1 directly.
There is no runtime environment switching — BASE_URL is a module-level constant. For multi-environment builds, consider using Expo’s app.config.js extra fields or a .env approach to inject the correct URL at build time.

The request() function

All API calls go through a single request() wrapper that handles URL construction, response parsing, and error throwing:
async function request(path, options = {}) {
  const url = `${BASE_URL}${path}`;
  const response = await fetch(url, options);
  let json;
  try { json = await response.json(); } catch {
    throw new Error(`Error ${response.status} — respuesta inválida`);
  }
  if (!response.ok || json.success === false) {
    throw new Error(json.message || `Error ${response.status}`);
  }
  return json.data;
}
The backend is expected to return responses in this shape:
{
  success: true | false,
  data: { /* payload */ },
  message: 'Human-readable error string'
}
request() extracts json.data on success. On failure — either a non-2xx HTTP status or success: false in the body — it throws an Error using the backend’s message string. If the response body cannot be parsed as JSON at all, it throws with the HTTP status code.
All API functions return json.data directly — you do not need to unwrap a wrapper object in your screens.

Auth headers

authHeaders() fetches a fresh Firebase ID token on every call and returns a headers object ready to pass to fetch:
async function authHeaders(extra = {}) {
  const user = getAuth().currentUser;
  if (!user) throw new Error('No hay usuario autenticado');
  const token = await user.getIdToken();
  return {
    'Content-Type': 'application/json',
    Authorization: `Bearer ${token}`,
    'X-User-Name': user.displayName || 'Cliente',
    ...extra,
  };
}
The token is not cached manually — getIdToken() handles refresh internally when the token is near expiry. The extra parameter lets callers merge in additional headers if needed.

Public vs authenticated endpoints

Some endpoints are publicly accessible and do not require a Firebase token. Others require authHeaders(). The pattern is consistent throughout the file:
// Public endpoint — no auth required
export async function fetchBakeries() {
  return request('/bakeries');
}

// Authenticated endpoint
export async function fetchCart() {
  const headers = await authHeaders();
  return request('/cart', { headers });
}
Public endpoints (no auth header):
  • fetchBakeries() — list all active bakeries
  • fetchNearbyBakeries(lat, lng, radius) — location-based bakery search
  • fetchProductsByBakery(bakeryId) — products for a bakery
  • fetchReviewsByBakery(bakeryId) — reviews for a bakery
  • fetchPromotions(bakeryId) — active promotions
Authenticated endpoints (require authHeaders()):
  • fetchCart(), addToCartApi(), updateCartItemApi(), clearCartApi()
  • createOrder(), fetchMyOrders(), fetchOrderById()
  • createPayment(), fetchPaymentByOrder()
  • fetchMyProfile(), updateMyProfile()
  • fetchFavorites(), toggleFavorite(), fetchFavoriteStatus(), removeFavorite()
  • fetchMyLoyaltyCards(), fetchLoyaltyCard()
  • fetchMyReviews(), canReviewOrder(), createReview()

Error handling

Errors from request() surface as standard JavaScript Error objects. The error message is the backend’s own message string when available, or a fallback with the HTTP status code. Screens and contexts should wrap API calls in try/catch and set appropriate error state:
try {
  const data = await fetchCart();
  setCart(data);
} catch (error) {
  console.error(error.message); // e.g. "No hay usuario autenticado"
  setError(error.message);
}
Network failures (no connectivity, DNS errors) are not caught by request() — they propagate as native fetch rejections. Handle these alongside backend errors in the same catch block.

Build docs developers (and LLMs) love