A robust RESTful API for the Recipe Spoon mobile application
Features β’ Installation β’ API Documentation β’ Deployment
Live API Base URL: https://recepie-api.onrender.com/api
The Recipe API is a comprehensive backend service built with Node.js, Express.js, and MongoDB that powers the Recipe Spoon mobile application. It provides secure authentication, recipe management, user favorites, search functionality, and more.
- β RESTful Architecture: Clean, standard API design
- π JWT Authentication: Secure user authentication and authorization
- π MongoDB Database: Scalable NoSQL database with Mongoose ODM
- π Production Ready: Deployed on Render with 99.9% uptime
- π Comprehensive Documentation: Well-documented endpoints
- π Advanced Search: Full-text search and filtering
- β‘ Performance Optimized: Caching and query optimization
-
User Authentication
- User registration with email validation
- Secure login with JWT tokens
- Password hashing with bcrypt
- Token-based session management
- Password reset functionality
-
Recipe Management
- Create, read, update, and delete recipes (CRUD)
- Rich recipe data (ingredients, instructions, images, videos)
- Recipe categorization and tagging
- Nutritional information
- Cooking time and difficulty levels
-
Search & Discovery
- Full-text search across recipes
- Filter by ingredients, cuisine, dietary preferences
- Sort by popularity, rating, date
- Advanced query parameters
-
User Features
- Favorite/bookmark recipes
- User recipe collections
- Recipe ratings and reviews
- User profiles and preferences
-
Media Handling
- Image upload and storage
- Video URL integration
- Cloudinary integration for media optimization
- Rate Limiting: Prevent API abuse
- Error Handling: Comprehensive error responses
- Validation: Request data validation with express-validator
- CORS: Cross-origin resource sharing enabled
- Logging: Request and error logging
- Health Check: Endpoint for monitoring
- Node.js (v18 or higher)
- MongoDB (v6 or higher)
- npm or yarn package manager
- MongoDB Atlas account (for cloud database) or local MongoDB
-
Clone the Repository
git clone https://github.com/Bisha18/recepie_api.git cd recepie_api -
Install Dependencies
npm install
-
Environment Configuration
Create a
.envfile in the root directory:# Server Configuration NODE_ENV=development PORT=5000 # Database MONGODB_URI=mongodb://localhost:27017/recipe_db # OR for MongoDB Atlas: # MONGODB_URI=mongodb+srv://<username>:<password>@cluster.mongodb.net/recipe_db # JWT Secret (use a strong random string) JWT_SECRET=your_super_secret_jwt_key_here JWT_EXPIRE=7d # Cloudinary (Optional - for image uploads) CLOUDINARY_CLOUD_NAME=your_cloud_name CLOUDINARY_API_KEY=your_api_key CLOUDINARY_API_SECRET=your_api_secret # Email Configuration (Optional - for password reset) EMAIL_HOST=smtp.gmail.com EMAIL_PORT=587 EMAIL_USER=your_email@gmail.com EMAIL_PASSWORD=your_app_password # Rate Limiting RATE_LIMIT_WINDOW_MS=900000 RATE_LIMIT_MAX_REQUESTS=100 # CORS ALLOWED_ORIGINS=http://localhost:19006,https://recipie-spoon.app
-
Database Setup
Make sure MongoDB is running:
# For local MongoDB mongod # For MongoDB Atlas, just ensure your connection string is correct
-
Seed Database (Optional)
npm run seed
-
Start Development Server
# Development mode with nodemon npm run dev # Production mode npm start
-
Verify Installation
curl http://localhost:5000/api/health
recepie_api/
βββ src/
β βββ config/ # Configuration files
β β βββ database.js # MongoDB connection
β β βββ cloudinary.js # Cloudinary setup
β β βββ email.js # Email service config
β βββ controllers/ # Route controllers
β β βββ authController.js
β β βββ recipeController.js
β β βββ userController.js
β β βββ favoriteController.js
β βββ models/ # Mongoose models
β β βββ User.js
β β βββ Recipe.js
β β βββ Favorite.js
β β βββ Review.js
β βββ routes/ # API routes
β β βββ auth.js
β β βββ recipes.js
β β βββ users.js
β β βββ favorites.js
β βββ middleware/ # Custom middleware
β β βββ auth.js # JWT authentication
β β βββ errorHandler.js
β β βββ validate.js # Request validation
β β βββ rateLimiter.js
β βββ utils/ # Utility functions
β β βββ emailService.js
β β βββ generateToken.js
β β βββ validators.js
β βββ seeds/ # Database seeders
β β βββ recipeSeed.js
β βββ app.js # Express app setup
βββ tests/ # Test files
β βββ auth.test.js
β βββ recipes.test.js
β βββ users.test.js
βββ .env.example # Environment variables template
βββ .gitignore
βββ package.json
βββ server.js # Entry point
βββ README.md
Development: http://localhost:5000/api
Production: https://recepie-api.onrender.com/api
All authenticated endpoints require a JWT token in the Authorization header:
Authorization: Bearer <your_jwt_token>
POST /api/auth/registerRequest Body:
{
"name": "John Doe",
"email": "john@example.com",
"password": "SecurePassword123!",
"confirmPassword": "SecurePassword123!"
}Response: 201 Created
{
"success": true,
"message": "User registered successfully",
"data": {
"user": {
"_id": "64f7b8c9e1234567890abcde",
"name": "John Doe",
"email": "john@example.com",
"createdAt": "2024-01-15T10:30:00.000Z"
},
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
}POST /api/auth/loginRequest Body:
{
"email": "john@example.com",
"password": "SecurePassword123!"
}Response: 200 OK
{
"success": true,
"message": "Login successful",
"data": {
"user": {
"_id": "64f7b8c9e1234567890abcde",
"name": "John Doe",
"email": "john@example.com"
},
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
}GET /api/auth/meHeaders: Authorization: Bearer <token>
Response: 200 OK
{
"success": true,
"data": {
"_id": "64f7b8c9e1234567890abcde",
"name": "John Doe",
"email": "john@example.com",
"favorites": [],
"createdAt": "2024-01-15T10:30:00.000Z"
}
}POST /api/auth/forgot-passwordRequest Body:
{
"email": "john@example.com"
}GET /api/recipesQuery Parameters:
page(number): Page number (default: 1)limit(number): Items per page (default: 20)search(string): Search termcategory(string): Filter by categorycuisine(string): Filter by cuisine typedifficulty(string): easy | medium | hardmaxTime(number): Maximum cooking time in minutessort(string): Sort field (e.g., -createdAt, rating)
Example:
GET /api/recipes?search=pasta&category=italian&limit=10&page=1
Response: 200 OK
{
"success": true,
"count": 45,
"pagination": {
"page": 1,
"limit": 10,
"total": 45,
"pages": 5
},
"data": [
{
"_id": "64f7b8c9e1234567890abcde",
"title": "Classic Spaghetti Carbonara",
"description": "A traditional Italian pasta dish...",
"image": "https://cloudinary.com/recipes/carbonara.jpg",
"videoUrl": "https://youtube.com/watch?v=...",
"prepTime": 10,
"cookTime": 20,
"servings": 4,
"difficulty": "medium",
"category": "italian",
"cuisine": "Italian",
"ingredients": [
{
"name": "Spaghetti",
"quantity": "400",
"unit": "g"
},
{
"name": "Eggs",
"quantity": "4",
"unit": "whole"
}
],
"instructions": [
{
"step": 1,
"description": "Bring a large pot of salted water to boil..."
}
],
"nutrition": {
"calories": 450,
"protein": 25,
"carbs": 55,
"fat": 15
},
"tags": ["pasta", "italian", "classic"],
"author": {
"_id": "64f7b8c9e1234567890abcde",
"name": "Chef Mario"
},
"rating": 4.8,
"reviewsCount": 156,
"createdAt": "2024-01-15T10:30:00.000Z",
"updatedAt": "2024-01-20T15:45:00.000Z"
}
]
}GET /api/recipes/:idResponse: 200 OK
{
"success": true,
"data": {
"_id": "64f7b8c9e1234567890abcde",
"title": "Classic Spaghetti Carbonara",
// ... full recipe details
}
}GET /api/recipes/search?q=chickenQuery Parameters:
q(string): Search query (required)limit(number): Results limit
Response: 200 OK
POST /api/recipesHeaders: Authorization: Bearer <token>
Request Body:
{
"title": "Homemade Pizza Margherita",
"description": "Authentic Italian pizza with fresh mozzarella",
"image": "https://cloudinary.com/...",
"videoUrl": "https://youtube.com/...",
"prepTime": 30,
"cookTime": 15,
"servings": 4,
"difficulty": "medium",
"category": "italian",
"cuisine": "Italian",
"ingredients": [
{
"name": "Pizza dough",
"quantity": "500",
"unit": "g"
},
{
"name": "Tomato sauce",
"quantity": "200",
"unit": "ml"
}
],
"instructions": [
{
"step": 1,
"description": "Preheat oven to 250Β°C..."
}
],
"nutrition": {
"calories": 280,
"protein": 12,
"carbs": 35,
"fat": 10
},
"tags": ["pizza", "italian", "vegetarian"]
}Response: 201 Created
PUT /api/recipes/:idHeaders: Authorization: Bearer <token>
Request Body: (Same as create, with fields to update)
Response: 200 OK
DELETE /api/recipes/:idHeaders: Authorization: Bearer <token>
Response: 200 OK
{
"success": true,
"message": "Recipe deleted successfully"
}GET /api/favoritesHeaders: Authorization: Bearer <token>
Response: 200 OK
{
"success": true,
"count": 5,
"data": [
{
"_id": "64f7b8c9e1234567890abcde",
"recipe": {
"_id": "64f7b8c9e1234567890abcdf",
"title": "Classic Spaghetti Carbonara",
"image": "https://...",
"rating": 4.8
},
"addedAt": "2024-01-15T10:30:00.000Z"
}
]
}POST /api/favorites/:recipeIdHeaders: Authorization: Bearer <token>
Response: 201 Created
DELETE /api/favorites/:recipeIdHeaders: Authorization: Bearer <token>
Response: 200 OK
GET /api/users/profileHeaders: Authorization: Bearer <token>
PUT /api/users/profileHeaders: Authorization: Bearer <token>
Request Body:
{
"name": "John Updated",
"bio": "Food enthusiast and home chef",
"avatar": "https://cloudinary.com/avatar.jpg",
"preferences": {
"dietaryRestrictions": ["vegetarian"],
"cuisinePreferences": ["italian", "japanese"]
}
}GET /api/users/my-recipesHeaders: Authorization: Bearer <token>
GET /api/categoriesResponse: 200 OK
{
"success": true,
"data": [
"Italian",
"Mexican",
"Chinese",
"Indian",
"Japanese",
"Mediterranean"
]
}GET /api/recipes/popularResponse: Similar to Get All Recipes
GET /api/healthResponse: 200 OK
{
"success": true,
"message": "API is running",
"timestamp": "2024-01-15T10:30:00.000Z",
"uptime": 86400,
"database": "connected"
}- JWT Tokens: Secure stateless authentication
- Password Hashing: Bcrypt with salt rounds
- Token Expiration: Automatic session timeout
- Role-Based Access: Admin and user roles
- Rate Limiting: Prevent brute force attacks
- CORS: Configured allowed origins
- Helmet: HTTP headers security
- Input Validation: Sanitization and validation
- SQL Injection Protection: Mongoose parameterized queries
- XSS Protection: Content Security Policy
- Environment Variables: Sensitive data in .env
- Secrets Management: Never commit secrets
- HTTPS Only: Enforced in production
# Run all tests
npm test
# Run tests with coverage
npm run test:coverage
# Run integration tests
npm run test:integration
# Run specific test file
npm test -- auth.test.js// Example test
describe('Auth Controller', () => {
describe('POST /api/auth/register', () => {
it('should register a new user', async () => {
const response = await request(app)
.post('/api/auth/register')
.send({
name: 'Test User',
email: 'test@example.com',
password: 'Password123!'
});
expect(response.status).toBe(201);
expect(response.body.success).toBe(true);
expect(response.body.data).toHaveProperty('token');
});
});
});-
Create Render Account
- Sign up at render.com
-
Create New Web Service
- Connect your GitHub repository
- Select the backend repository
-
Configure Build Settings
Build Command: npm install Start Command: npm start -
Add Environment Variables
- Add all variables from
.envfile - Make sure to use production values
- Add all variables from
-
Deploy
- Click "Create Web Service"
- Render will automatically deploy
# Login to Heroku
heroku login
# Create new app
heroku create recepie-api
# Add MongoDB addon
heroku addons:create mongolab:sandbox
# Set environment variables
heroku config:set JWT_SECRET=your_secret_here
# Deploy
git push heroku main
# Open app
heroku open- Launch EC2 instance (Ubuntu)
- SSH into instance
- Install Node.js and MongoDB
- Clone repository
- Install dependencies
- Set up PM2 for process management
- Configure Nginx as reverse proxy
- Set up SSL with Let's Encrypt
- Database Indexing: Indexes on frequently queried fields
- Query Optimization: Efficient MongoDB queries
- Caching: Redis for frequently accessed data
- Pagination: Limit data transfer
- Compression: Gzip compression enabled
- CDN: Cloudinary for media delivery
All errors follow a consistent format:
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Email is required",
"statusCode": 400,
"details": {
"field": "email",
"value": ""
}
}
}400- Bad Request401- Unauthorized403- Forbidden404- Not Found409- Conflict (duplicate)422- Validation Error429- Too Many Requests500- Internal Server Error
Logs are written to:
- Console (development)
- Log files (production)
- External service (optional)
// Example log format
{
"timestamp": "2024-01-15T10:30:00.000Z",
"level": "info",
"method": "GET",
"path": "/api/recipes",
"statusCode": 200,
"responseTime": "45ms",
"userId": "64f7b8c9e1234567890abcde"
}{
name: String (required),
email: String (required, unique),
password: String (required, hashed),
avatar: String,
bio: String,
role: String (default: 'user'),
preferences: {
dietaryRestrictions: [String],
cuisinePreferences: [String]
},
createdAt: Date,
updatedAt: Date
}{
title: String (required),
description: String (required),
image: String,
videoUrl: String,
prepTime: Number,
cookTime: Number,
servings: Number,
difficulty: String (enum: easy/medium/hard),
category: String,
cuisine: String,
ingredients: [{
name: String,
quantity: String,
unit: String
}],
instructions: [{
step: Number,
description: String
}],
nutrition: {
calories: Number,
protein: Number,
carbs: Number,
fat: Number
},
tags: [String],
author: ObjectId (ref: User),
rating: Number,
reviewsCount: Number,
createdAt: Date,
updatedAt: Date
}Contributions are welcome! Please follow these guidelines:
- Fork the repository
- Create a feature branch:
git checkout -b feature/amazing-feature - Commit changes:
git commit -m 'Add amazing feature' - Push to branch:
git push origin feature/amazing-feature - Open a Pull Request
- Use ES6+ features
- Follow Airbnb JavaScript Style Guide
- Write meaningful comments
- Add JSDoc documentation for functions
This project is licensed under the MIT License - see LICENSE file.
Bisha18
- GitHub: @Bisha18
- Frontend: recipie_spoon
- Express.js - Fast web framework
- MongoDB - Database
- Mongoose - ODM
- JWT - Authentication
- Render - Hosting platform
- Cloudinary - Media management
For issues or questions:
- Open an Issue
- Email: support@recipespoon.com