Kahawa Connect POS is an Expo SDK 56 React Native app for running a small cafe workflow. It includes a cashier POS screen, order queue, inventory tracking, simulated M-Pesa payment handling, sales ledger, and a local Node backend API backed by SQLite.
The project is currently best understood as a functional prototype: it has working frontend screens and a backend with persisted local SQLite state, but it is not yet production-ready because it does not include authentication, a real database, real M-Pesa Daraja integration, or deployment configuration.
| Area | Status | Notes |
|---|---|---|
| Expo app | Working prototype | Runs through Expo Router on mobile and web |
| POS screen | Working prototype | Menu, cart, checkout, payment method selection |
| Operations screen | Working prototype | Order queue, status updates, inventory, sales view |
| Backend API | Working local backend | Node HTTP server backed by SQLite |
| Persistence | SQLite database | Uses backend/kahawa.sqlite; suitable for development and stronger than JSON, but still needs production hardening |
| M-Pesa | Simulated | Confirms with mock PIN flow and generated receipt code |
| Tests | Not implemented | TypeScript validation works, but no test suite exists |
| Production readiness | Not ready | Needs auth, DB, real payments, logging, deployment, CI |
- Expo SDK 56
- React 19
- React Native 0.85
- Expo Router
- TypeScript strict mode
- Node.js backend using built-in
http,path, andnode:sqlite - SQLite persistence
Important Expo note: this project follows the local instruction in AGENTS.md to reference Expo SDK 56 documentation before code changes.
.
├── app.json
├── package.json
├── README.md
├── backend/
│ ├── README.md
│ └── server.js
├── src/
│ ├── app/
│ │ ├── _layout.tsx
│ │ ├── index.tsx
│ │ └── explore.tsx
│ ├── components/
│ ├── constants/
│ ├── hooks/
│ ├── services/
│ │ └── kahawaApi.ts
│ └── store/
│ └── kahawaStore.tsx
└── assets/The app models this cafe workflow:
- Cashier opens the POS screen.
- Cashier selects menu items.
- Items are added to the cart.
- Cashier enters customer details.
- Cashier selects payment method:
M-Pesa,Cash, orCard. - Checkout creates an order.
- Inventory is reserved or deducted depending on payment type.
- Operations staff move orders through the kitchen queue.
- Served orders appear in the ledger and sales report.
File: src/app/index.tsx
The POS screen includes:
- category filtering:
All,Coffee,Tea & Dawa,Snacks - menu cards with stock availability labels
- cart quantity controls
- customer name input
- item customization input
- payment selector
- cash amount and change calculation
- checkout action
This screen currently still uses the local React context state. A typed backend API client exists in src/services/kahawaApi.ts, but the frontend has not yet been fully migrated from local context to API-driven state.
File: src/app/explore.tsx
The operations screen includes:
- active order queue
- order status actions
- completed order history
- sales summary
- inventory management
- restock inputs
- transaction ledger
Order statuses are:
Pending Payment
Brewing
Ready
Served
CancelledFile: src/store/kahawaStore.tsx
The original app state is stored in a React context provider. It contains:
- menu data
- inventory data
- current cart
- order history
- mock M-Pesa state
- cart operations
- checkout logic
- inventory deduction
- restocking logic
This frontend store is useful for demo behavior, but it is not reliable enough for a real POS because data resets on reload and business rules are enforced only on the client.
File: backend/server.js
The backend adds server-side business logic and SQLite persistence. It stores data in:
backend/kahawa.sqliteThat database file is created automatically when the API first starts. The backend uses Node 22 built-in node:sqlite, so it does not require Express or an external SQLite package. Node currently labels node:sqlite as experimental, so production work should either pin an appropriate Node runtime or move to a mature database driver.
Run the backend:
npm run apiDefault base URL:
http://localhost:4000/apiYou can override the port:
PORT=4010 npm run apiYou can override the data file:
KAHAWA_DB_FILE=/tmp/kahawa.sqlite npm run api| Method | Endpoint | Purpose |
|---|---|---|
GET |
/api/health |
Check whether the API is running |
GET |
/api/menu |
Return menu items |
GET |
/api/inventory |
Return inventory with available, reserved, and low fields |
PATCH |
/api/inventory/:id/restock |
Add stock to one inventory item |
POST |
/api/orders |
Create an order |
GET |
/api/orders |
List orders; supports status, from, and to filters |
GET |
/api/orders/:id |
Get one order |
PATCH |
/api/orders/:id/status |
Update order status |
POST |
/api/payments/mpesa/:id/confirm |
Confirm a pending M-Pesa order |
POST |
/api/payments/mpesa/:id/cancel |
Cancel a pending M-Pesa order |
GET |
/api/ledger |
Return served orders only |
GET |
/api/reports/sales |
Return sales totals and totals by payment method |
The backend corrects several important flaws from the original frontend-only logic:
- order IDs are generated from a persisted counter
- cash checkout records
cashPaidandchangeDue - customizations are stored in order items
- M-Pesa orders reserve inventory first
- M-Pesa confirmation deducts reserved inventory
- cancelling pending M-Pesa orders releases reserved inventory
- cancelling deducted orders restores inventory
- finalized orders cannot be changed
Servedorders are the only orders counted in the ledger and sales report- reports support
fromandtodate filters
Valid status movement:
Pending Payment -> Brewing -> Ready -> Served
Pending Payment -> Cancelled
Brewing -> Cancelled
Ready -> CancelledThe backend blocks invalid transitions such as serving an order before it is ready or brewing an M-Pesa order before payment confirmation.
File: src/services/kahawaApi.ts
This file maps frontend actions to backend endpoints. It exports kahawaApi with methods such as:
getMenu()getInventory()createOrder()getOrders()updateOrderStatus()confirmMpesaPayment()cancelMpesaPayment()getLedger()getSalesReport()
Default API URL:
http://localhost:4000/apiFor Expo, set this environment variable if needed:
EXPO_PUBLIC_KAHAWA_API_URL=http://localhost:4000/apiFor Android emulator, localhost from the app may not point to your machine. You may need:
http://10.0.2.2:4000/apiFor a physical phone, use your computer's LAN IP address.
Install dependencies:
npm installStart the backend:
npm run apiStart Expo in a second terminal:
npm run startRun web:
npm run webRun Android:
npm run androidRun iOS:
npm run iosValidate TypeScript:
npx tsc --noEmitCheck backend syntax:
node --check backend/server.js- Expo app structure is valid.
- The app has clear POS and operations screens.
- Menu, inventory, orders, and payments are modeled coherently.
- The local backend starts successfully with
npm run api. - API endpoints support the key POS workflows.
- Backend inventory handling is more correct than the original frontend-only logic.
- The backend can persist local state across restarts using
backend/data.json. - TypeScript validation passes.
- The frontend still primarily uses
KahawaProviderlocal state. - The backend API client exists, but the UI has not yet been fully wired to call it.
- M-Pesa behavior is realistic as a simulation, but not real payment processing.
- Card payment exists as a payment method, but there is no card authorization flow.
- Sales reports are available through the backend, but the frontend screen still computes some values locally.
- real Safaricom Daraja integration
- authentication and roles
- user accounts
- server-side sessions
- audit log table
- receipt printing
- offline sync
- CI pipeline
- automated tests
- production deployment setup
-
Frontend is not fully API-driven yet.
The app has a backend and API client, but the existing screens still read from local context. The next important step is to replace local store actions with API calls and refresh server state after mutations.
-
Persistence now uses SQLite.
This is a real database and is a major improvement over JSON persistence. For a multi-terminal production POS, PostgreSQL or another client/server database is still preferable.
-
M-Pesa is simulated.
The current flow confirms a 4-digit PIN locally and generates a fake receipt code. A real implementation needs Safaricom Daraja STK Push, callback handling, transaction reconciliation, retries, and failure states.
-
No authentication.
Any client can call any endpoint. Production needs user login, role-based permissions, and endpoint authorization.
-
No automated tests.
The backend business rules should be tested, especially inventory deduction, reservation, cancellation, and order status transitions.
-
No concurrency protection.
The JSON backend is not safe for multiple simultaneous cashiers. A real database transaction should protect inventory and order writes.
-
No production logging or monitoring.
The backend returns API errors, but it does not yet have structured logs, request IDs, metrics, or alerting.
- Wire
src/services/kahawaApi.tsintoKahawaProvideror replace the provider with API-backed state. - Add loading and error states in both screens.
- Add backend tests for checkout, stock, cancellation, reports, and status transitions.
- Add migration tooling and consider PostgreSQL for multi-device production use.
- Add authentication and staff roles.
- Add real M-Pesa Daraja integration.
- Add a
.env.examplefile for API configuration. - Add CI checks for TypeScript, linting, and backend tests.
This project is viable as a prototype and learning project. It now has enough backend logic to map the main app actions to real endpoints and enforce the most important business rules on the server.
It is not yet viable as a real cafe POS until the frontend is connected to the backend, database migrations/backups are formalized, payment integration is real, and authentication plus tests are added.