Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/AndrewwCO/Pana-Baker/llms.txt

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

All network requests in Panahashi Baker go through a single ApiService class defined in src/services/api.js. Before you run the app against a real server, you need to update one constant with your backend’s URL. Everything else — auth headers, error handling, and endpoint routing — is already wired up.

Setting the base URL

The first line you need to change is the API_BASE constant at the top of src/services/api.js:
const API_BASE = "http://10.0.2.2:8080/api/v1"; // 👈 Cambia esto
Replace the value with the base URL of your deployed backend, for example https://api.yourbakery.com/api/v1.
10.0.2.2 is the Android emulator’s alias for localhost on your development machine. It works when you run the app in Android Studio’s emulator against a local server, but it will not reach a remote server and it does not work on a physical device or in iOS Simulator. Change this to your real URL before running on a device or building for production.
To avoid hard-coding the URL, add it to a .env file in the project root and read it via expo-constants (already in package.json). Create .env:
API_BASE=https://api.yourbakery.com/api/v1
Then in src/services/api.js, replace the constant with:
import Constants from "expo-constants";
const API_BASE = Constants.expoConfig?.extra?.apiBase ?? "http://10.0.2.2:8080/api/v1";
And expose the variable in app.json under expo.extra.apiBase.

How requests are made

Every endpoint method calls ApiService.call(), which wraps fetch and automatically attaches auth headers:
// src/services/api.js
async call(path, options = {}) {
  const res = await fetch(`${API_BASE}${path}`, {
    ...options,
    headers: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${this.token}`,
      "X-User-Name": this.userName,
      ...(options.headers || {}),
    },
    body: options.body ? JSON.stringify(options.body) : undefined,
  });

  if (res.status === 401) throw new Error("NO_AUTH");
  if (!res.ok) {
    const err = await res.json().catch(() => ({}));
    throw new Error(err.message || `HTTP ${res.status}`);
  }

  const data = await res.json();
  return data.data ?? data;
}
Two headers are always present on every request:
  • Authorization: Bearer <token> — the Firebase ID token obtained from firebaseUser.getIdToken().
  • X-User-Name — the bakery owner’s display name, used by the backend for logging and display purposes.

Setting the token

You do not call api.setToken() yourself. The onAuthStateChanged listener in src/services/auth.js calls it automatically whenever the auth state changes:
const token = await firebaseUser.getIdToken();
api.setToken(token);
api.setUserName(firebaseUser.displayName || "Baker");
When the user logs out, api.setToken(null) is called to clear the token before the next request.

401 error handling

If the server returns a 401 Unauthorized response, call() throws new Error("NO_AUTH"). You can catch this error anywhere in your screens to redirect the user to the login screen:
try {
  const bakery = await api.getBakery();
} catch (err) {
  if (err.message === "NO_AUTH") {
    // Token expired or revoked — send the user back to login
  }
}

Endpoint groups

All endpoints are methods on the exported api singleton (export const api = new ApiService()).

Bakery

MethodEndpointDescription
getBakery()GET /bakeries/meFetch the authenticated bakery’s profile.
updateBakery(body)PATCH /bakeries/meUpdate name, description, phone, address, or hours.
toggleOpen(isOpen)PATCH /bakeries/me/openOpen or close the bakery.

Orders

MethodEndpointDescription
getActiveOrders()GET /orders/bakery/activeAll active (non-completed) orders.
getAllOrders(page)GET /orders/bakery?page=&pageSize=100Paginated full order history.
getPendingOrders()GET /orders/bakery/pendingOrders awaiting confirmation.
updateOrderStatus(id, status)PATCH /orders/:id/statusAdvance an order through its lifecycle.
verifyQr(qrCode)POST /orders/verify-qrMark an order as completed via QR scan.
setEstimatedReady(id, estimatedReadyAt)PATCH /orders/:id/estimated-readySet a pickup time estimate for an order.

Products

MethodEndpointDescription
getMyProducts()GET /products/myAll products belonging to the bakery.
createProduct(body)POST /productsCreate a new product.
updateProduct(id, body)PATCH /products/:idEdit product details.
updateStock(id, stock)PATCH /products/:id/stockSet stock quantity.
deleteProduct(id)DELETE /products/:idRemove a product.
seedProducts()POST /products/seedLoad sample products for testing.

Stats

MethodEndpointDescription
getBakeryStats()GET /stats/bakeryRevenue, order counts, top products, and hourly breakdown.

Promotions

MethodEndpointDescription
getMyPromotions()GET /promotions/meAll promotions for the bakery.
createPromotion(body)POST /promotionsCreate a percentage, fixed-amount, or happy-hour discount.
togglePromotion(id)PATCH /promotions/:id/toggleActivate or deactivate a promotion.
deletePromotion(id)DELETE /promotions/:idRemove a promotion.

Reviews

MethodEndpointDescription
getReviews(bakeryId)GET /reviews?bakeryId=Fetch reviews for a given bakery.

Upload

Image uploads use multipart/form-data instead of JSON and bypass the call() helper:
MethodEndpointDescription
uploadProductImage(productId, uri)POST /upload/product/:idUpload a product photo.
uploadBakeryLogo(uri)POST /upload/bakery/logoUpload the bakery logo.
uploadBakeryBanner(uri)POST /upload/bakery/bannerUpload the bakery banner image.

Firebase Auth setup

Configure token generation so the API receives valid credentials.

Building with EAS

Package the app for Android and iOS distribution.

Build docs developers (and LLMs) love