Yakult App is a full-stack sales management platform built for Yakult distributors. It consists of three tightly connected layers: a React Native (Expo) mobile front-end that sales representatives carry in the field, an Express 5 REST API that enforces business logic and authentication, and a MySQL database that persists every product, client, order, and report. Each layer has a single, clearly defined responsibility, and they communicate exclusively through a JSON HTTP interface — meaning the mobile app never touches the database directly and the server never pushes data without a client request (except through the notification polling model).Documentation Index
Fetch the complete documentation index at: https://mintlify.com/160906/Yakultt-App/llms.txt
Use this file to discover all available pages before exploring further.
System Layers
Mobile App
React Native 0.74 + Expo 51. Handles UI, local state, token persistence, and all API calls through
services/db.ts. Navigation is file-based via Expo Router.REST API
Express 5 server on port 3000. Validates JWT tokens, runs business-logic checks, and queries MySQL. CORS is open to all origins for development flexibility.
MySQL Database
yakult_db schema with seven tables. Schema is created and migrated automatically on every server start via ensureSchema() — no manual ALTER TABLE scripts needed.Auth Layer
JWT tokens signed with a server secret, valid for 30 days. Tokens are stored in
AsyncStorage on the device and sent on every request as Authorization: Bearer <token>.Request Flow
Every action in the mobile app follows this path:User interaction in a screen
A screen inside
app/(tabs)/ calls a method on one of the service objects exported from services/db.ts (e.g., OrdenesDB.agregar(order)).HTTP request with auth headers
services/db.ts builds the request headers and calls the API base URL. The following headers are attached on every call:| Header | Purpose |
|---|---|
Content-Type | Always application/json — tells the server to parse the request body as JSON |
x-github-token | Always sent as 'anonymous'; required by the CORS allowedHeaders policy |
Authorization: Bearer <token> | JWT identity proof, verified server-side via verificarToken() |
x-user-id | Numeric user ID used to scope notification queries without decoding the JWT on every read |
API route handler
The Express router for the matched route (e.g.,
POST /api/ordenes) validates the token, applies business-logic checks, and runs the appropriate SQL query via the db pool.Database query
MySQL executes the query against
yakult_db and returns rows. The route handler formats the result as JSON and responds to the client.REST API — Express 5
The server entry point isbackend/server.js. It configures CORS, mounts all route modules, and calls ensureSchema() before the HTTP listener starts:
CORS is configured with
origin: '*' to allow development from any host (Expo Go, simulators, dev tunnels). In a production deployment this should be narrowed to the specific app origin.Mounted Routes
| Prefix | Module | Responsibility |
|---|---|---|
/api/auth | routes/auth.js | Registration, login, user management |
/api/productos | routes/productos.js | Product CRUD and stock |
/api/clientes | routes/clientes.js | Client CRUD and activation toggle |
/api/ordenes | routes/ordenes.js | Order lifecycle, repartidor assignment, status changes |
/api/notificaciones | routes/notificaciones.js | Per-user notification list, mark-as-read |
/api/reportes | routes/reportes.js | Saved report generation and retrieval |
Auto-Migration System
backend/schema.js exports a single function, ensureSchema(), that is called once during server startup. It uses CREATE TABLE IF NOT EXISTS for every table and a helper, addColumnIfMissing(), to ALTER existing tables when new columns are introduced. An indexExists() helper gates every CREATE INDEX call as well. The result is fully idempotent — the same code runs safely on a brand-new database and on a database that has been running in production for months.
ensureSchema() rejects (e.g., the database is unreachable), the process exits with code 1 rather than starting in a broken state.
Mobile App — React Native / Expo
The mobile app lives inyakult-app/ and follows a three-tier layer structure:
| Layer | Files | Role |
|---|---|---|
| Screens | app/(tabs)/*.tsx, app/auth.tsx | UI, user interaction, local loading state |
| Services | services/db.ts | All fetch calls to the API, header assembly, 401 handling |
| Context | context/AuthContext.tsx, context/ToastContext.tsx | Global auth state, toast notifications |
Root Layout
app/_layout.tsx wraps the entire navigation tree with both context providers so every screen has access to the authenticated user and the toast system:
Tab Navigation
The(tabs) group contains the main app screens. The tab bar shows five entries; perfil, admin, and ventas are accessible via router.push() but hidden from the bar:
| Screen | Route | Access |
|---|---|---|
| Dashboard | /(tabs)/ | All roles |
| Productos | /(tabs)/productos | Master, Promotor |
| Clientes | /(tabs)/clientes | Master, Promotor |
| Órdenes | /(tabs)/ordenes | All roles |
| Reportes | /(tabs)/reportes | Master, Promotor |
| Ventas | /(tabs)/ventas | Master |
| Admin | /(tabs)/admin | Master only (redirects others) |
| Perfil | /(tabs)/perfil | All roles |
Token Persistence
AuthContext stores the JWT and the serialized user object in AsyncStorage under the keys yakult_token and yakult_usuario. On app boot, it reads both keys and rehydrates the session:
401 response from the API automatically triggers logout(), clears AsyncStorage, and redirects the user to the login screen.