Search millions of recipes. Save your favourites. Create your own.
Powered by intelligent smart-caching proxy β checks your local database first, fetches from the DummyJSON API only when needed, and saves new results automatically.
- β¨ Features
- ποΈ Architecture
- π οΈ Tech Stack
- π Project Structure
- π Getting Started
- βοΈ Environment Variables
- π‘ API Reference
- π Smart Caching Logic
- π CI/CD Pipeline
- πΏ Branching Strategy
- π€ Contributing
- π License
| Feature | Description |
|---|---|
| π Smart Search | Searches local DB first, falls back to DummyJSON API, auto-saves new results |
| π‘ Typeahead Suggestions | 300ms debounced dropdown with thumbnail previews, source badges & keyboard navigation |
| βΎοΈ Infinite Scroll | Paginated recipe grid (8 per page) with Intersection Observer β no buttons needed |
| ποΈ Recipe Detail Modal | Full view with hero image, ingredient checklist, numbered instruction steps |
| βοΈ Full CRUD | Create, Read, Update, Delete recipes with a polished modal form |
| π·οΈ Source Badges | Every card shows whether a recipe came from your vault or the DummyJSON API |
| π¨ GSAP Animations | Page-load hero animation + staggered card entrance effects |
| π± Responsive Design | Mobile-first grid layout with glassmorphism dark UI |
| π CI/CD Pipeline | Full GitHub Actions pipeline with lint, build, audit, and deploy stages |
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β BROWSER β
β β
β React + Vite + Tailwind CSS + Shadcn UI + GSAP β
β βββββββββββββββ ββββββββββββββββ βββββββββββββββββ β
β β SearchBar β β RecipeGrid β β RecipeDetail β β
β β (typeahead) β β(infinite β β Modal β β
β ββββββββ¬βββββββ β scroll) β βββββββββββββββββ β
β β ββββββββββββββββ β
ββββββββββββΌβββββββββββββββββββββββββββββββββββββββββββββββββ
β HTTP (Vite proxy in dev / direct in prod)
ββββββββββββΌβββββββββββββββββββββββββββββββββββββββββββββββββ
β EXPRESS REST API β
β Node.js Β· ES Modules β
β β
β GET /api/recipes β paginated all recipes β
β GET /api/recipes/search β smart hybrid search β
β GET /api/recipes/suggestions β typeahead β
β POST /api/recipes β create β
β PUT /api/recipes/:id β update β
β DELETE /api/recipes/:id β delete β
β GET /api/health β health check β
ββββββββββββ¬βββββββββββββββββββββββββββ¬ββββββββββββββββββββββ
β β
ββββββββΌβββββββ ββββββββββΌβββββββββ
β MongoDB β β DummyJSON API β
β Atlas β β (external data) β
β (primary) β β fetched only β
βββββββββββββββ β on cache miss β
βββββββββββββββββββ
| Technology | Version | Purpose |
|---|---|---|
| Node.js | 20.x LTS | Runtime |
| Express.js | 4.x | REST API framework |
| Mongoose | 8.x | MongoDB ODM |
| MongoDB Atlas | Cloud | Database |
| Axios | 1.x | External API calls |
| dotenv | 16.x | Environment config |
| cors | 2.x | Cross-origin requests |
| Technology | Version | Purpose |
|---|---|---|
| React | 19 | UI framework |
| Vite | 8 | Build tool & dev server |
| Tailwind CSS | v4 | Utility-first styling |
| Shadcn UI | latest | Component primitives |
| GSAP | 3.x | Animations |
| Axios | 1.x | HTTP client |
| Lucide React | latest | Icon library |
| Technology | Purpose |
|---|---|
| GitHub Actions | CI/CD pipeline |
| Render.com | Backend hosting |
| Vercel | Frontend hosting |
| Dependabot | Automated dependency updates |
RecipeApp/
β
βββ .github/
β βββ workflows/
β β βββ ci.yml # π CI β runs on every push & PR
β β βββ cd-production.yml # π CD β deploys main β Render + Vercel
β β βββ cd-staging.yml # π§ͺ CD β deploys develop β preview
β β βββ security-audit.yml # π Weekly security scans
β βββ dependabot.yml # π€ Auto dependency updates
β
βββ backend/
β βββ models/
β β βββ Recipe.js # Mongoose schema & model
β βββ routes/
β β βββ recipes.js # All API route handlers
β βββ server.js # Express app entry point
β βββ .env.example # Environment variable template
β βββ package.json
β
βββ frontend/
β βββ src/
β β βββ components/
β β β βββ Navbar.jsx # Top navigation bar
β β β βββ SearchBar.jsx # Search input + portal dropdown
β β β βββ RecipeGrid.jsx # Masonry card grid with GSAP
β β β βββ RecipeCard.jsx # Individual recipe card
β β β βββ RecipeModal.jsx # Create / Edit form modal
β β β βββ RecipeDetailModal.jsx # Full recipe viewer
β β β βββ ui/ # Shadcn UI primitives
β β βββ hooks/
β β β βββ useRecipes.js # Axios API hook
β β βββ lib/
β β β βββ utils.js # cn() helper
β β βββ App.jsx # Root component + state management
β β βββ main.jsx # React entry point
β β βββ index.css # Global styles + Tailwind directives
β βββ .env.example
β βββ vite.config.js
β βββ package.json
β
βββ .gitignore
βββ README.md
- Node.js v20+ β nodejs.org
- MongoDB Atlas account β mongodb.com/atlas (free tier is fine)
- Git β git-scm.com
git clone https://github.com/cusaldmsr/RecipeApp.git
cd RecipeAppcd backend
# Install dependencies
npm install
# Create your environment file
cp .env.example .envEdit .env with your values (see Environment Variables below):
# Start the development server
npm run dev
# β Backend running at http://localhost:5000
# β API available at http://localhost:5000/api/recipesOpen a new terminal:
cd frontend
# Install dependencies
npm install
# Start the Vite dev server
npm run dev
# β Frontend running at http://localhost:5173http://localhost:5173
Note: The Vite dev server proxies all
/api/*requests tohttp://127.0.0.1:5000automatically. Both servers must be running.
# MongoDB Atlas connection string
# Get it from: Atlas dashboard β Connect β Drivers β copy URI
MONGO_URI=mongodb+srv://<username>:<password>@cluster0.xxxxxx.mongodb.net/recipeDB?retryWrites=true&w=majority&appName=Cluster0
# Express server port (default: 5000)
PORT=5000# Your deployed backend URL β NOT needed for local dev (Vite proxy handles it)
VITE_API_BASE_URL=https://your-backend-url.onrender.com
β οΈ Never commit real.envfiles. Only.env.examplefiles are tracked by git.
Base URL (local): http://localhost:5000/api
GET /health{ "status": "OK", "message": "Recipe API is running π½οΈ" }GET /recipes?page=1&limit=8| Query Param | Type | Default | Description |
|---|---|---|---|
page |
number | 1 |
Page number |
limit |
number | 8 |
Results per page (max 50) |
Response:
{
"source": "database",
"recipes": [ { "...recipe objects..." } ],
"total": 42,
"page": 1,
"totalPages": 6,
"hasMore": true
}GET /recipes/search?q=pastaRuns a parallel query against local DB and DummyJSON API. Returns categorised results.
Response:
{
"dbRecipes": [ { "...already saved recipes..." } ],
"apiRecipes": [ { "...new results fetched from API..." } ],
"total": 12
}GET /recipes/suggestions?q=pasReturns up to 6 matching recipes from local DB for the dropdown (requires β₯ 2 chars).
Response:
{
"suggestions": [
{ "_id": "...", "name": "Pasta Carbonara", "image": "...", "source": "DummyJSON" }
]
}POST /recipes
Content-Type: application/json{
"name": "Spaghetti Bolognese",
"ingredients": ["500g mince", "1 onion", "2 garlic cloves", "400g canned tomatoes"],
"instructions": ["Brown the mince.", "Add onion and garlic.", "Simmer with tomatoes."],
"prepTimeMinutes": 10,
"cookTimeMinutes": 30,
"servings": 4,
"image": "https://example.com/image.jpg"
}Response: 201 Created β saved recipe object.
PUT /recipes/:id
Content-Type: application/jsonPass any fields to update. Returns updated recipe.
DELETE /recipes/:id{ "message": "Recipe deleted successfully.", "id": "..." }This is the core intelligence of RecipeVault. On every search:
User searches "pasta"
β
ββββ 1. Query local MongoDB (fast, always)
β β dbRecipes (already saved)
β
ββββ 2. Query DummyJSON API (parallel, always)
β
βββ Filter: remove recipes already in DB (by external ID)
β β only truly NEW results
β
βββ Upsert new results into MongoDB
β (next search for "pasta" hits DB only)
β
βββ Return new results as apiRecipes
Frontend displays:
βββββββββββββββββββββββββββββββββββββββββββββββββββββ
β 5 from your vault Β· 3 new from API β
β [DB results first] [API results after] β
βββββββββββββββββββββββββββββββββββββββββββββββββββββ
Result: The first search for any keyword is slightly slower (fetches from API). Every subsequent search for the same keyword is instant (served from MongoDB).
Four GitHub Actions workflows run automatically:
Every push / PR to any branch
β
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββ
β π ci.yml (Continuous Integration) β
β β
β backend-validate βββ β
β backend-audit βββ€ β
β backend-test βββΌβββΊ β
CI Quality Gate β
β frontend-build βββ€ (blocks PR merge β
β frontend-audit βββ€ if any fail) β
β frontend-test βββ β
βββββββββββββββββββ¬βββββββββββββββββββββββββββββββ
β CI passes on `main`
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββ
β π cd-production.yml β
β β
β [Manual Approval Required] β
β deploy-backend β Render.com β
β deploy-frontend β Vercel β
β smoke-test β health check endpoints β
β create-release β GitHub Release + changelog β
ββββββββββββββββββββββββββββββββββββββββββββββββββ
CI passes on `develop`
β
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββ
β π§ͺ cd-staging.yml β
β β
β deploy-backend β Render Staging service β
β deploy-frontend β Vercel Preview URL β
β PR comment β posts preview URL on PR β
ββββββββββββββββββββββββββββββββββββββββββββββββββ
Every Monday 08:00 UTC
β
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββ
β π security-audit.yml β
β β
β audit-backend β npm audit (JSON report) β
β audit-frontend β npm audit (JSON report) β
β secret-scan β detect committed .env filesβ
β outdated-report β npm outdated for both β
ββββββββββββββββββββββββββββββββββββββββββββββββββ
Go to Settings β Secrets and variables β Actions and add:
| Secret | Description |
|---|---|
RENDER_BACKEND_DEPLOY_HOOK_URL |
Render deploy hook URL for the backend service |
RENDER_BACKEND_SERVICE_URL |
Your Render backend public URL |
VERCEL_TOKEN |
Vercel personal access token |
VERCEL_ORG_ID |
Vercel org/team ID |
VERCEL_PROJECT_ID |
Vercel project ID |
VITE_API_BASE_URL |
Production backend URL injected at build time |
CI works with zero secrets. Secrets are only required to activate the CD (deploy) workflows.
main β production branch (protected, requires PR + CI pass)
β
βββ develop β staging branch (auto-deploys to preview)
β
βββ feature/recipe-ratings
βββ fix/search-dropdown-zindex
βββ chore/update-dependencies
Workflow:
- Create a feature branch from
develop - Open a PR β CI runs automatically
- Merge to
developβ staging auto-deploys - Open PR from
developβmainβ manual approval β production deploys
Contributions are welcome! Here's how:
# 1. Fork the repository on GitHub
# 2. Clone your fork
git clone https://github.com/<your-username>/RecipeApp.git
cd RecipeApp
# 3. Create a feature branch
git checkout -b feature/your-feature-name
# 4. Make your changes, then commit
git add .
git commit -m "feat: describe your change"
# 5. Push and open a Pull Request
git push origin feature/your-feature-nameThis project follows Conventional Commits:
| Prefix | Use for |
|---|---|
feat: |
New features |
fix: |
Bug fixes |
ci: |
CI/CD changes |
chore: |
Maintenance, dependency updates |
docs: |
Documentation only |
refactor: |
Code restructuring |
Backend (Jest):
cd backend
npm install -D jest supertest
# Add tests to backend/tests/Frontend (Vitest):
cd frontend
npm install -D vitest @testing-library/react @testing-library/jest-dom
# Add tests to frontend/src/__tests__/This project is licensed under the MIT License.
MIT License β Copyright (c) 2026 cusaldmsr
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software.
Made with β€οΈ using the MERN Stack