🏆 3rd Place Winner - MongoDB Track - 🏆
- About the Project
- Tech Stack
- Architecture
- Repository Layout
- Setup & Local Development
- Environment Variables
- Running the API
- Database Schema
- API Reference
- Authentication Flow
- Deployment to Google Cloud Run
Elara AI is a service me and @gaiborjouse built for the AI in Action 2025 Hackathon. A user submits a medical concern (e.g. "I have a headache") and receives:
- the highest-rated medicinal plant (from our MongoDB Atlas dataset)
- a Gemini-generated recipe using that plant
- an optional PDF download of the recipe
All code lives in backend/.
✨ This is my first-ever FastAPI project ✨, so every pattern and decision in here reflects a newcomer's learning journey.
Data Source: Our medicinal plant database is sourced from Plants For A Future (PFAF), a comprehensive online database of useful plants and their properties.
| Layer / Concern | Technology |
|---|---|
| Language | Python 3.11 |
| Web Framework | FastAPI + Uvicorn |
| Data Store | MongoDB Atlas (shared M10) |
| Hosting | Google Cloud Run (Docker container) |
| LLM Services | Vertex AI — Gemini Pro / Flash / Lite |
| Auth | OAuth2 Password Flow · JWT · Bcrypt |
| Email Service | SMTP (Gmail/SendGrid) |
| PDF Rendering | WeasyPrint + Jinja2 template |
| Container Reg. | Artifact Registry |
| Secrets | Google Secret Manager (+ .env for local dev) |
| CI / CD (opt-in) | Cloud Run |
What calls what?
/getRecommendations→extract()→classifyCondition()→bestPlant()→ MongoDB/getRecipe→getRecipe()→ Vertex AI/downloadRecipePDF→ WeasyPrint- Auth utilities touch the
userscollection. - Email verification uses SMTP for sending verification emails.
backend/
├─ app.py # FastAPI entry-point & routers
├─ Dockerfile
├─ requirements.txt
├─ auth/ # 🔐 authentication helpers
│ ├─ hashing.py # bcrypt hashing
│ ├─ jwttoken.py # JWT creation / verification
│ └─ oauth.py # OAuth2PasswordBearer dependency
├─ utils/
│ ├─ symptoms.py # Gemini Lite symptom extractor
│ ├─ classification.py # Gemini Flash condition classifier
│ ├─ recommender.py # Mongo aggregation + hazard filter
│ └─ recipe.py # Gemini Flash recipe generator
├─ templates/
│ └─ recipe.html # Jinja2 → PDF template
├─ static/
└─ database/
├─ ingest.py # **database seed script**
└─ data/ # raw CSV / JSON plant datasets
├─ pfaf_plants_merged.csv
└─ pfaf_plants_merged_2.csv
The repo works perfectly fine without a dedicated virtual environment; just ensure you're on Python 3.11+ and install requirements:
cd backend
pip install -r requirements.txt
# Copy & fill secrets
cp .env.example .env
# └─ set: MONGODB_URI, DB_NAME, COLL_NAME,
# GOOGLE_PROJECT_ID, SERVICE_ACCOUNT_JSON,
# SECRET_KEY, ACCESS_TOKEN_EXPIRE_MINUTES,
# SMTP_SERVER, SMTP_PORT, SMTP_USERNAME, SMTP_PASSWORD,
# FRONTEND_URL
# Seed the database (optional in dev)
python database/ingest.py
# Launch the API
uvicorn app:app --reloadSwagger UI lives at http://localhost:8000/docs.
| Variable | Purpose |
|---|---|
MONGODB_URI |
Atlas SRV connection string |
DB_NAME, COLL_NAME |
database & collection names |
GOOGLE_PROJECT_ID |
Vertex AI project id |
SERVICE_ACCOUNT_JSON |
path/JSON creds for IAM |
SECRET_KEY |
JWT signing key |
ACCESS_TOKEN_EXPIRE_MINUTES |
token TTL |
SMTP_SERVER |
SMTP server for email sending |
SMTP_PORT |
SMTP port (usually 587) |
SMTP_USERNAME |
SMTP username/email |
SMTP_PASSWORD |
SMTP password/app password |
FRONTEND_URL |
Frontend URL for verification links |
PORT |
gunicorn/uvicorn port (Cloud Run) |
Local (see Setup & Local Development) or Docker:
docker build -t elara-api backend
docker run --env-file backend/.env -p 8000:8080 elara-api- plants
latin_name_search,common_name_search,medicinal_rating_searchedibility_rating_search,Edible Uses,Known Hazards,plant_url
- users (verified users only)
email,username,hashed_password,email_verified,created_at,verified_at
- pending_users (TTL = 24 hours)
email,username,hashed_password,verification_token,verification_token_expires,created_at
- saved_recipes (TTL = 10 days)
recipe,deletedAt,user_id
| Verb | Endpoint | Auth | Purpose |
|---|---|---|---|
| POST | /register |
– | create user with email verification |
| POST | /verify-email |
– | verify email with token |
| POST | /resend-verification |
– | resend verification email |
| POST | /login |
– | issue JWT (requires verified email) |
| GET | /me |
✅ | return current user |
| POST | /getRecommendations |
✅ | LLM adapters → best plant |
| POST | /getRecipe |
✅ | generate recipe via Gemini |
| POST | /downloadRecipePDF |
✅ | render recipe → PDF |
| POST | /saveRecipe |
✅ | persist recipe |
| DELETE | /deleteRecipe/{id} |
✅ | soft delete (sets deletedAt) |
| GET | /recentlyDeletedRecipes |
✅ | list TTL-pending deletions |
/register→hashing.pybcrypt → insert inpending_users→ send verification email./verify-email→ validate token → move user frompending_userstousers→ mark as verified./login→ check user exists inuserscollection → returnaccess_tokenviajwttoken.py.- Protected routes use
oauth.py(OAuth2PasswordBearer). - Token verified →
jwttoken.verifyToken().
gcloud run deploy elarabackend --source . --region us-central1 --service-account ${SERVICE_ACCOUNT_EMAIL} --allow-unauthenticated --project ${GCP_PROJECT_ID} --set-secrets="MONGODB_URI=mongodb-uri:latest,DB_NAME=db-name:latest,COLL_NAME=coll-name:latest,SECRET_KEY=jwt-secret-key:latest,ALGORITHM=jwt-algorithm:latest,EXPIRE_MINUTES=jwt-expire-minutes:latest,PROJECT=project:latest,SMTP_SERVER=smtp-server:latest,SMTP_PORT=smtp-port:latest,SMTP_USERNAME=smtp-username:latest,SMTP_PASSWORD=smtp-password:latest,FRONTEND_URL=frontend-url:latest"Cloud Run autoscales 0 → N (cold starts ≈1 s).

