CookBook is a modern recipe discovery and management platform where users can explore 166+ recipes across 8 cuisines, save their favourites, create their own recipes, and watch video tutorials — all in one place.
Features • Tech Stack • Getting Started • API Reference • Project Structure • Screenshots
- 🔐 JWT Authentication — Secure register, login, and protected routes
- 🔍 Smart Search — Search recipes by name, ingredient, cuisine, or tag
- 🎛️ Filters — Filter by cuisine type and difficulty level with pagination
- 📖 166+ Recipes — Pre-loaded dataset covering 8 world cuisines
- ❤️ Favourites — Save and manage your favourite recipes
- ✏️ Full CRUD — Create, edit, and delete your own recipes
- 🎬 YouTube Integration — Every recipe links directly to a YouTube tutorial
- 🌙 Dark Mode — Full dark/light mode toggle
- 📊 Nutrition Info — Calories, protein, carbs, and fat per serving
- ✅ Ingredient Checklist — Tick off ingredients while you cook
- 📱 Fully Responsive — Works on mobile, tablet, and desktop
| Technology | Purpose |
|---|---|
| React 18 + Vite | UI framework and build tool |
| Tailwind CSS | Utility-first styling |
| React Router DOM | Client-side routing |
| Axios | HTTP requests to the backend |
| React Context API | Global state management (auth, theme) |
| React Toastify | Toast notifications |
| React Icons | Icon library |
| Technology | Purpose |
|---|---|
| Node.js | JavaScript runtime |
| Express.js | Web framework and REST API |
| MongoDB | NoSQL database |
| Mongoose | ODM for schema design and queries |
| JSON Web Token | Stateless authentication |
| bcryptjs | Password hashing |
| express-validator | Input validation |
| dotenv | Environment variable management |
| cors | Cross-origin request handling |
| nodemon | Auto-restart during development |
┌─────────────────────────────────────────┐
│ React Frontend │
│ (Vite + Tailwind + Context API) │
└──────────────────┬──────────────────────┘
│ HTTP / REST API
│ (Axios + JWT Token)
┌──────────────────▼──────────────────────┐
│ Express Backend │
│ │
│ Route → Middleware → Controller │
│ ↓ │
│ Mongoose ODM │
└──────────────────┬──────────────────────┘
│
┌──────────────────▼──────────────────────┐
│ MongoDB │
│ users │ recipes │ favorites │
└─────────────────────────────────────────┘
Client Request
→ Express Router (matches URL)
→ Middleware (CORS, JSON parse, Auth check)
→ Controller (business logic)
→ Mongoose (database query)
→ Response (JSON with status code)
cookbook/
│
├── start.js # Single command to run everything
├── package.json # Root scripts
│
├── backend/
│ ├── server.js # Express app, middleware, DB connection
│ ├── seed.js # Seeds 166 recipes into the database
│ ├── .env # Environment variables (not committed)
│ ├── package.json
│ │
│ ├── config/
│ │ └── db.js # MongoDB connection
│ │
│ ├── models/
│ │ ├── User.js # User schema (bcrypt pre-save hook)
│ │ ├── Recipe.js # Recipe schema (text index for search)
│ │ └── Favorite.js # Favorite schema (unique compound index)
│ │
│ ├── controllers/
│ │ ├── auth.controller.js # register, login, getMe
│ │ ├── recipe.controller.js # CRUD + search + pagination
│ │ ├── favorite.controller.js # add, remove, list favorites
│ │ └── user.controller.js # profile, change password
│ │
│ ├── routes/
│ │ ├── auth.routes.js
│ │ ├── recipe.routes.js
│ │ ├── favorite.routes.js
│ │ └── user.routes.js
│ │
│ └── middleware/
│ └── auth.middleware.js # JWT protect + adminOnly
│
└── frontend/
├── index.html
├── vite.config.js # Proxy /api → localhost:5000
├── tailwind.config.js
├── postcss.config.js
├── package.json
│
└── src/
├── main.jsx
├── App.jsx # Router setup + ThemeProvider
├── index.css # Tailwind imports + dark mode overrides
│
├── context/
│ ├── AuthContext.jsx # Global auth state (user, token, login, logout)
│ └── ThemeContext.jsx # Dark/light mode toggle
│
├── services/
│ ├── api.js # Axios instance with auto JWT header
│ └── recipeService.js # All API call functions
│
├── routes/
│ └── PrivateRoute.jsx # Redirects unauthenticated users
│
├── components/
│ ├── Navbar.jsx # Responsive navbar with dropdown + dark toggle
│ ├── RecipeCard.jsx # Recipe card with YouTube link + save button
│ ├── RecipeForm.jsx # Shared form for create and edit
│ ├── SearchBar.jsx # Search input with quick-filter tags
│ └── Spinner.jsx # Loading indicator
│
└── pages/
├── Home.jsx # Browse, search, filter, paginate recipes
├── Login.jsx
├── Register.jsx
├── RecipeDetail.jsx # Full recipe view with YouTube + checklist
├── CreateRecipe.jsx
├── EditRecipe.jsx
├── Favorites.jsx
├── MyRecipes.jsx
├── Profile.jsx # Edit profile + change password
└── NotFound.jsx
Make sure you have the following installed:
- Node.js v18 or higher
- MongoDB running locally OR a MongoDB Atlas connection string
Step 1 — Clone the repository
git clone https://github.com/yourusername/cookbook.git
cd cookbookStep 2 — Configure environment variables
Open backend/.env and update the values:
PORT=5000
MONGO_URI=mongodb://localhost:27017/cookbook
JWT_SECRET=your_super_secret_key_here
JWT_EXPIRES_IN=7d
NODE_ENV=developmentIf using MongoDB Atlas, replace
MONGO_URIwith your Atlas connection string:mongodb+srv://username:password@cluster0.xxxxx.mongodb.net/cookbook
Step 3 — Install all dependencies (one command)
npm run install:allStep 4 — Start the entire application (one command)
node start.jsThis single command will:
- ✅ Start the backend server on port 5000
- ✅ Seed 166 recipes from the dataset into MongoDB
- ✅ Start the frontend on port 5173
Step 5 — Open in browser
http://localhost:5173
Press
Ctrl + Cto stop everything.
http://localhost:5000/api
| Method | Endpoint | Access | Description |
|---|---|---|---|
| POST | /auth/register |
Public | Register a new user |
| POST | /auth/login |
Public | Login and receive JWT token |
| GET | /auth/me |
Private | Get current logged-in user |
| Method | Endpoint | Access | Description |
|---|---|---|---|
| GET | /recipes |
Public | Get all recipes (search, filter, paginate) |
| GET | /recipes/:id |
Public | Get single recipe by ID |
| GET | /recipes/my-recipes |
Private | Get recipes created by logged-in user |
| POST | /recipes |
Private | Create a new recipe |
| PUT | /recipes/:id |
Private (Owner) | Update a recipe |
| DELETE | /recipes/:id |
Private (Owner) | Delete a recipe |
Query Parameters for GET /recipes:
?search=pasta → search by name, ingredient, tag, cuisine
?cuisine=Italian → filter by cuisine
?difficulty=Easy → filter by difficulty
?page=1&limit=9 → pagination
| Method | Endpoint | Access | Description |
|---|---|---|---|
| GET | /favorites |
Private | Get all saved recipes |
| POST | /favorites/:recipeId |
Private | Save a recipe |
| DELETE | /favorites/:recipeId |
Private | Remove from favorites |
| Method | Endpoint | Access | Description |
|---|---|---|---|
| GET | /users/profile |
Private | Get user profile |
| PUT | /users/profile |
Private | Update name and bio |
| PUT | /users/change-password |
Private | Change password |
# Add Authorization header with Bearer token
curl -X GET http://localhost:5000/api/recipes/my-recipes \
-H "Authorization: Bearer YOUR_JWT_TOKEN_HERE"{
"success": true,
"total": 3,
"pages": 1,
"recipes": [
{
"_id": "64abc123...",
"title": "Butter Chicken",
"cuisine": "Indian",
"difficulty": "Medium",
"cookingTime": 45,
"nutrition": {
"calories": 420,
"protein": "32g",
"carbs": "14g",
"fat": "24g"
},
"createdBy": {
"_id": "64xyz789...",
"name": "Sai"
}
}
]
}{
name: String (required),
email: String (required, unique),
password: String (hashed with bcrypt, hidden from responses),
bio: String,
role: String (enum: 'user', 'admin'),
createdAt: Date
}{
title: String (required),
description: String,
image: String (URL),
cuisine: String (enum: Indian, Italian, Mexican...),
difficulty: String (enum: Easy, Medium, Hard),
cookingTime: Number (minutes),
servings: Number,
ingredients: [{ name: String, quantity: String }],
steps: [{ stepNumber: Number, instruction: String }],
tags: [String],
nutrition: { calories, protein, carbs, fat },
createdBy: ObjectId → ref: User,
isPublic: Boolean,
createdAt: Date
}{
userId: ObjectId → ref: User,
recipeId: ObjectId → ref: Recipe,
createdAt: Date
}
// Unique compound index on { userId, recipeId }
// Prevents duplicate saves1. User registers → password hashed with bcrypt (12 salt rounds)
→ user saved to MongoDB
→ JWT token generated and returned
2. User logs in → password compared with bcrypt.compare()
→ JWT token generated (expires in 7 days)
→ token stored in localStorage on frontend
3. Protected request → Axios sends token in Authorization header
→ protect middleware extracts and verifies token
→ user fetched from DB and attached to req.user
→ controller executes with req.user available
4. Token invalid/expired → 401 Unauthorized response
→ frontend redirects to /login
The application comes pre-loaded with 166 recipes across 8 cuisines:
| Cuisine | Count |
|---|---|
| Mediterranean | 39 |
| Italian | 18 |
| American | 17 |
| Indian | 13 |
| Japanese | 9 |
| Mexican | 10 |
| Chinese | 8 |
| Thai | 6 |
| Other (World) | 46 |
Each recipe includes detailed descriptions, step-by-step instructions, ingredient lists, nutrition values, and a direct YouTube search link.
| Member | Role |
|---|---|
| Member 1 | Backend & API Design Engineer |
| Member 2 | Authentication & Security Engineer |
| Member 3 | Database & Data Modeling Engineer |
| Member 4 | Frontend Architecture & State Management |
| Member 5 | UI/UX & Feature Implementation |
This project is built for academic purposes as part of a Full Stack Web Development course.
Made with ❤️ using the MERN Stack
⭐ Star this repo if you found it helpful!