Reviewed-on: #10
💊 FarmaFinder
A web application to search for medicines from the official Spanish CIMA database and find which pharmacies sell them.
✨ Features
Web App (Desktop/PWA)
- 🔍 Real-time medicine search from CIMA API (Agencia Española de Medicamentos)
- 💾 Redis caching for improved performance
- 📍 View pharmacies that sell a specific medicine
- 💰 See prices and stock availability
- 📱 Responsive design for mobile and desktop
- ⚙️ Admin Panel - Manage pharmacies and link medicines
- 🔐 Secure authentication - Login required to access admin features
- Add, edit, and delete pharmacies
- Search medicines from CIMA database
- Link medicines to pharmacies with prices and stock
Mobile App (React Native)
- 📱 Native iOS/Android experience with Expo
- 🔍 Medicine search with real-time results
- 🗺️ Interactive map with pharmacy markers
- 📷 Barcode scanner for quick medicine lookup
- 🔔 Push notifications for availability alerts
- 🔐 Biometric authentication (Face ID / Touch ID)
- 💾 Offline cache for favorite medicines
🛠️ Tech Stack
Backend
- Runtime: Node.js + Express
- Database: SQLite (for pharmacies and relationships)
- Cache: Redis
- External API: CIMA (Centro de Información online de Medicamentos de la AEMPS)
Frontend (Web/PWA)
- Framework: React + Vite
- Mobile wrapper: Capacitor (for hybrid mobile builds)
Frontend (Mobile - React Native)
- Framework: Expo SDK 57 + React Native
- Navigation: Expo Router v4
- State: Zustand
- HTTP: Axios + TanStack Query
- Maps: react-native-maps
- Camera: expo-camera (barcode scanning)
- Auth: expo-local-authentication (biometrics)
- Notifications: expo-notifications
- Build: EAS Build
📋 Prerequisites
- Node.js (v18 or higher)
- npm or yarn
- Redis server (v6.0 or higher)
🐳 Docker Setup (Recommended)
Runs the full stack (backend, frontend, Redis) with a single command.
Prerequisites: Docker and Docker Compose v2.
# Copy and configure environment (optional — defaults work for local dev)
cp backend/.env.example backend/.env
# Edit backend/.env to set SESSION_SECRET and any other vars
docker compose up --build
App available at http://localhost:3000.
First run — create an admin user:
docker compose exec backend node create-admin.js
# Default: admin / admin123 — change after first login
Seed sample pharmacies:
docker compose exec backend node seed.js
Stop:
docker compose down
Database is persisted in a named Docker volume (backend_data). To wipe it:
docker compose down -v
🚀 Manual Setup Instructions
1. Install Redis
On Ubuntu/Debian:
sudo apt-get update
sudo apt-get install redis-server
sudo systemctl start redis-server
sudo systemctl enable redis-server
On macOS (using Homebrew):
brew install redis
brew services start redis
On Windows: Download and install from: https://redis.io/download
Using Docker:
docker run -d -p 6379:6379 redis:alpine
Verify Redis is running:
redis-cli ping
# Should respond with: PONG
2. Install Application Dependencies
Install backend dependencies:
cd backend
npm install
Install frontend dependencies:
cd ../frontend
npm install
3. Configure Environment (Optional)
Create a .env file in the backend directory if you need custom Redis settings:
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=
SESSION_SECRET=your-secret-key-here
4. Initialize Database
Seed the database with sample pharmacies:
cd backend
npm run seed
Create admin user:
npm run create-admin
This creates a default admin user with:
- Username:
admin - Password:
admin123
⚠️ Important: Change the default password after first login!
You can customize credentials using environment variables:
ADMIN_USERNAME=myadmin ADMIN_PASSWORD=mypassword npm run create-admin
5. Running the Application
Start Redis (if not running):
redis-server
Start the backend server:
cd backend
npm start
The backend will run on http://localhost:3001
Start the frontend development server:
cd frontend
npm run dev
The frontend will run on http://localhost:3000
Open your browser and navigate to http://localhost:3000
🎯 How to Use
Public Search
- Type the name of a medicine in the search bar
- The app will search the CIMA database in real-time
- Click on a medicine to see which pharmacies have it
- View prices and stock availability
Admin Panel
- Click the "⚙️ Admin Panel" button
- Login with your credentials (default:
admin/admin123) - Pharmacies Tab: Add, edit, or delete pharmacies
- Medicines Tab: Search medicines from the CIMA database
- Link Medicine Tab: Associate medicines with pharmacies and set prices/stock
📁 Project Structure
FarmaFinder/
├── docker-compose.yml # Full stack: backend + frontend + Redis
├── backend/
│ ├── Dockerfile
│ ├── server.js # Express server and API routes
│ ├── cima-service.js # CIMA API integration with Redis cache
│ ├── redis-client.js # Redis connection configuration
│ ├── seed.js # Database seeding script
│ ├── create-admin.js # Admin user creation script
│ ├── .env.example # Environment variable template
│ └── package.json
├── frontend/ # React + Vite (Desktop/PWA)
│ ├── Dockerfile
│ ├── nginx.conf # Nginx config (Docker): serves SPA + proxies /api
│ ├── src/
│ │ ├── components/ # React components
│ │ │ ├── admin/ # Admin panel components
│ │ │ ├── PharmacyMap.jsx # Leaflet map (OpenStreetMap)
│ │ │ └── ...
│ │ ├── views/ # View components (Public/Admin)
│ │ ├── App.jsx # Main app component
│ │ └── main.jsx # Entry point
│ ├── index.html
│ └── package.json
├── frontend-mobile/ # Expo + React Native (iOS/Android)
│ ├── app/
│ │ ├── _layout.tsx # Root layout with providers
│ │ ├── (tabs)/ # Bottom tab navigation
│ │ │ ├── index.tsx # Home (medicine search)
│ │ │ ├── map.tsx # Pharmacy map
│ │ │ └── profile.tsx # User profile
│ │ ├── medicine/[id].tsx # Medicine detail
│ │ ├── pharmacy/[id].tsx # Pharmacy detail
│ │ ├── auth/ # Login/Register screens
│ │ └── scanner.tsx # Barcode scanner
│ ├── components/ # Reusable UI components
│ ├── services/ # API and business logic
│ ├── store/ # Zustand state management
│ ├── hooks/ # Custom React hooks
│ ├── constants/ # Theme and config
│ ├── types/ # TypeScript types
│ ├── eas.json # EAS Build configuration
│ └── package.json
├── android/ # Capacitor Android project
├── ios/ # Capacitor iOS project
└── README.md
🔌 API Endpoints
Public API
GET /api/medicines/search?q=<query>- Search medicines from CIMA API (cached in Redis)GET /api/medicines/:nregistro- Get medicine details from CIMAGET /api/medicines/:nregistro/pharmacies- Get pharmacies selling a medicineGET /api/pharmacies- Get all pharmacies
Authentication API
POST /api/auth/login- Login (requires username and password)POST /api/auth/logout- LogoutGET /api/auth/check- Check authentication status
Admin API (All require authentication)
POST /api/admin/pharmacies- Add a new pharmacyPUT /api/admin/pharmacies/:id- Update a pharmacyDELETE /api/admin/pharmacies/:id- Delete a pharmacyGET /api/admin/medicines?q=<query>- Search medicines from CIMA (for admin)GET /api/admin/pharmacies/:id/medicines- Get medicines linked to a pharmacyPOST /api/admin/pharmacy-medicines- Link medicine to pharmacyPUT /api/admin/pharmacy-medicines/:id- Update price/stockDELETE /api/admin/pharmacy-medicines/:id- Remove medicine from pharmacy
💾 Database Schema
SQLite Tables
pharmacies
id: Integer (Primary Key)name: Text (Pharmacy name)address: Text (Full address)phone: Text (Contact phone)latitude: Real (GPS coordinate)longitude: Real (GPS coordinate)
pharmacy_medicines
id: Integer (Primary Key)pharmacy_id: Integer (Foreign Key → pharmacies.id)medicine_nregistro: Text (CIMA medicine registration number)medicine_name: Text (Cached medicine name)price: Real (Price in EUR)stock: Integer (Available units)
users
id: Integer (Primary Key)username: Text (Unique)password_hash: Text (Bcrypt hashed)created_at: DateTime
Redis Cache Structure
medicines:search:{query}- Search results (TTL: 1 hour)medicine:{nregistro}- Medicine details (TTL: 24 hours)
🔧 Architecture Changes
Migration from Local Database to CIMA API
The application now uses the CIMA (Centro de Información online de Medicamentos) API as the source of truth for medicine data, with Redis caching for performance:
Benefits:
- ✅ Always up-to-date medicine information
- ✅ Official data from Spanish health authorities
- ✅ Reduced database maintenance
- ✅ Fast responses thanks to Redis cache
- ✅ Fallback to stale cache if API is down
Changes:
- Medicines are no longer stored locally in SQLite
- Medicine searches query the CIMA API
- Results are cached in Redis for performance
pharmacy_medicinesnow usesmedicine_nregistro(CIMA registration number) instead of localmedicine_id
📱 Mobile App Setup (React Native)
Prerequisites
- Node.js (v18 or higher)
- npm or yarn
- Expo CLI:
npm install -g expo-cli - EAS CLI:
npm install -g eas-cli - iOS: Xcode (Mac only) + CocoaPods
- Android: Android Studio + Android SDK
Quick Start
# Install all dependencies (backend + web + mobile)
npm run install:all
# Start mobile development server
npm run dev:mobile
# Scan QR code with Expo Go app (iOS/Android)
Development Build
For native features (camera, biometrics, notifications), use a development build:
# Install EAS CLI
npm install -g eas-cli
# Login to Expo
eas login
# Create development build
eas build --profile development --platform ios
eas build --profile development --platform android
Project Structure
frontend-mobile/
├── app/ # Expo Router screens
│ ├── (tabs)/ # Bottom tab navigation
│ ├── auth/ # Login/Register
│ ├── medicine/ # Medicine detail
│ ├── pharmacy/ # Pharmacy detail
│ └── scanner.tsx # Barcode scanner
├── components/ # Reusable UI components
├── services/ # API and business logic
├── store/ # Zustand state management
├── hooks/ # Custom React hooks
├── constants/ # Theme and config
└── types/ # TypeScript types
Native Features
| Feature | Implementation |
|---|---|
| Barcode Scanner | expo-camera with CameraView |
| Push Notifications | expo-notifications |
| Biometrics | expo-local-authentication |
| Maps | react-native-maps |
| Secure Storage | expo-secure-store |
EAS Build Profiles
| Profile | Platform | Build Type | Use Case |
|---|---|---|---|
development |
iOS | Simulator | Local testing on Mac |
development |
Android | APK | Local testing on device |
preview |
Android | APK | Internal testing & sharing |
production |
Android | AAB | Google Play Store submission |
Note: iOS builds require Apple Developer account ($99/year) and are configured separately.
Environment Configuration
The mobile app uses the same backend API as the web app. Configure the API URL in:
// frontend-mobile/constants/config.ts
const ENV = {
development: {
API_BASE_URL: 'http://localhost:3001/api',
},
production: {
API_BASE_URL: 'https://your-production-api.com/api',
},
};
🚀 Production Deployment (Mobile)
Distribution Flow
Development → EAS Build → App Store / Google Play → User Device
The mobile app is a native application that runs directly on the device. No Docker or server needed - users download it from the app stores.
Step 1: Setup Expo Account
# Install EAS CLI
npm install -g eas-cli
# Create account at https://expo.dev
# Login
eas login
Step 2: Initialize EAS Project
cd frontend-mobile
eas init
This generates a projectId - add it to app.json:
"extra": {
"eas": {
"projectId": "your-project-id"
}
}
Step 3: Configure Credentials
Android (Google Play):
- Create developer account ($25 one-time fee)
- Create project in Google Cloud Console
- Enable Play Developer API
- Download
google-service-account.json - Place in
frontend-mobile/directory
iOS (App Store):
- Join Apple Developer Program ($99/year)
- Create App ID in Apple Developer portal
- Generate certificates and provisioning profiles
- Update
eas.jsonwith your credentials
Step 4: Build for Production
# Android (Google Play)
eas build --profile production --platform android
# iOS (App Store)
eas build --profile production --platform ios
Step 5: Submit to Stores
# Submit to Google Play
eas submit --profile production --platform android
# Submit to App Store
eas submit --profile production --platform ios
OTA Updates (Without App Store Review)
Push updates directly to users without going through store review:
# Install expo-updates
npx expo install expo-updates
# Send update
eas update --branch production --message "Fix: improved search"
Useful Commands
| Command | Description |
|---|---|
eas build:list |
View previous builds |
eas build:cancel <id> |
Cancel a build |
eas submit:list |
View previous submissions |
eas update |
Send OTA update |
🐛 Troubleshooting
Redis Connection Issues
If you see "Redis Client Error":
# Check if Redis is running
redis-cli ping
# Start Redis if needed
redis-server
CIMA API Timeout
If medicine searches are slow or failing:
- Check your internet connection
- The CIMA API may be temporarily unavailable
- The app will use cached data if available
Database Issues
Reset the database:
cd backend
rm database.sqlite
npm run seed
npm run create-admin
📝 Development
Backend
Start with auto-reload:
cd backend
npm run dev
Frontend (Web/PWA)
Start development server:
cd frontend
npm run dev
Frontend (Mobile - React Native)
Install all dependencies:
npm run install:all
Start Expo dev server:
npm run dev:mobile
Start for specific platform:
npm run dev:mobile:android # Android emulator
npm run dev:mobile:ios # iOS simulator
Build with EAS:
npm run build:mobile # Production build
Submit to stores:
npm run submit:android # Google Play
npm run submit:ios # App Store
Clear Redis cache
redis-cli FLUSHALL
🌐 External Resources
- CIMA API: https://cima.aemps.es/
- Redis Documentation: https://redis.io/documentation
- React Documentation: https://react.dev
- Express Documentation: https://expressjs.com
📄 License
ISC