A modern, beautiful restaurant rating and review platform built with Next.js, TailwindCSS, and Framer Motion. Features a stunning dark theme with glass morphism effects and smooth animations.
- Modern Dark UI: Beautiful dark theme with glass morphism effects
- Responsive Design: Works perfectly on mobile and desktop
- Smooth Animations: Framer Motion powered animations and transitions
- Restaurant Discovery: Browse and search restaurants by cuisine, location, and price
- Review System: Rate restaurants and read user reviews
- Interactive Components: Hover effects, smooth transitions, and modern UX
- Framework: Next.js 14 (App Router)
- Styling: TailwindCSS with custom glass effects
- Animations: Framer Motion
- Icons: React Icons
- Language: TypeScript
- Styling: Custom CSS with glass morphism utilities
- Database: MongoDB Atlas with Mongoose ODM
- Authentication: NextAuth.js with JWT sessions
- Security: bcrypt for password hashing
- Glass Morphism: Beautiful translucent containers with backdrop blur
- Dark Theme: Elegant dark color scheme with accent colors
A modern restaurant rating and review platform built with Next.js (App Router), TailwindCSS, Framer Motion and MongoDB.
This README focuses on the backend logic (models, APIs, auth and image upload flow) and the main frontend structure and components used to interact with the backend.
- Overview
- Architecture
- Backend: Models & Schemas
- Backend: Key API endpoints
- Authentication
- Image upload flow (Cloudinary)
- Frontend structure & important components
- Environment variables
- Local development & testing
- Notes & next steps
The app stores restaurants and user reviews in MongoDB via Mongoose models. Reviews may include multiple images (stored as arrays of image URLs). A Cloudinary-backed upload endpoint is used to store images and return a hosted secure_url which is saved on each Review document.
- Next.js app (app directory, React client components)
- API routes implemented under
app/api/*(server runtime) - Mongoose models under
models/ - Authentication using NextAuth (Credentials provider) with JWT strategy
- Image uploads handled by a server-side Cloudinary uploader at
/api/review-upload
The following Mongoose models exist (fields trimmed to essentials):
models/Review.js(Review)- userId: ObjectId (ref 'User') β required
- username: String β required
- restaurant: String β required
- rating: Number (1-5) β required
- comment: String (trimmed, max 500) β required
- images: [String] β array of image URLs (default: [])
- rating_breakdown: { taste, presentation, service, ambiance, value } β optional per-category scores (1-5)
- timestamps: createdAt, updatedAt
- Indexes: { userId, createdAt }, { restaurant, createdAt }, { createdAt }, and a compound index including
images.0to speed image-based lookups
models/Restaurant.js(Restaurant)- name: String β required
- cuisine: String β required
- location: String β required
- priceRange: String β one of ['$', '$$', '$$$', '$$$$']
- description: String
- addedBy: ObjectId (ref 'User') β required
- rating: Number (avg rating)
- totalReviews: Number
- image: String (main image URL)
- imageThumb: String (thumbnail)
- imageBlur: String (base64 tiny image for blur placeholder)
- featured: Boolean
- timestamps and several performance indexes (unique name+location, featured, rating sorting, full-text index for search)
models/User.js(User)- email: String β required, unique index
- password: String β hashed (bcrypt)
- timestamps
Notes:
- Reviews hold the authoritative image URLs for review photos (an array of strings). Images are uploaded to Cloudinary and the returned
secure_urlis stored inReview.images.
All API code lives under app/api/*.
POST /api/review-upload- Purpose: Upload a single image to Cloudinary and return a hosted URL.
- Request: JSON body { file: string } where
fileis a Data URL (e.g.data:image/jpeg;base64,...) or a remote http(s) URL. - Response: 200 { secure_url: string } or error 4xx/5xx.
- Notes: Uses Cloudinary SDK server-side. The endpoint verifies Cloudinary config and returns helpful diagnostic info on errors.
GET /api/reviews- Query params:
userId,restaurant(optional filters) - Response: JSON array of reviews (sorted by createdAt desc). Each review may include
imagesarray.
- Query params:
POST /api/reviews(auth required)- Body: { restaurant, rating, comment, images?: string[], rating_breakdown?: {...} }
- Behavior: Validates input, creates a new Review (user taken from session), saves images array (if provided), increments/updates Restaurant rating and totalReviews (creates or updates restaurant record as needed), and returns the created review.
- Response: 201 { message, review }
PATCH /api/reviews(auth required)- Body: { id, rating?, comment?, images?, rating_breakdown?, restaurantName?, restaurantLocation? }
- Behavior: Verifies ownership, updates review fields. For images, the API accepts a complete array and replaces the review's images with the provided array (server-side limits to 6). If restaurant name/location change, it attempts to update/migrate restaurant metadata.
DELETE /api/reviews?id=<id>(auth required)- Behavior: Verifies ownership, attempts to delete Cloudinary resources for the review (if Cloudinary config present), deletes the review, and recalculates or deletes the associated restaurant record depending on remaining reviews.
Error handling: API routes return JSON error messages and appropriate HTTP status codes. Authentication is enforced via NextAuth server session checks for modifying routes.
- Implemented in
lib/auth.jsusing NextAuth with multiple providers:- Credentials Provider: Email & password authentication with bcrypt hashing
- Google OAuth 2.0: Sign in with Google account
- Both providers connect to MongoDB and create/update user records
- Session strategy: JWT (
session.strategy = 'jwt') with callbacks addinguser.id,user.username, andproviderinto the token/session - Pages:
signInandsignUpconfigured under/auth/*routes with support for both credential and Google OAuth flows - Google OAuth users are automatically created in the database on first sign-in
- Client selects files and the client code (e.g.
AddReviewModal,EditReviewModal) reads each file as a Data URL using FileReader. - For each file the client calls
POST /api/review-uploadwith body{ file: dataUrl }. - The API uploads to Cloudinary using the server SDK; the endpoint returns
{ secure_url }. - Client collects the returned secure URLs and sends them as part of the review payload to
POST /api/reviewsorPATCH /api/reviews.
Server-side deletion: When a review is deleted, the DELETE /api/reviews handler tries to extract Cloudinary public_id from stored image URLs and call Cloudinary API to remove those resources (if Cloudinary credentials are present).
Key files and components (high level):
app/β Next.js app router pages and API routes. Notable pages:app/explore/page.tsxβ Explore restaurants listingapp/restaurant/[id]/page.tsxβ Restaurant detail and reviews
components/SimpleRestaurantCard.tsxβ Card used in explore gridsAddReviewModal.tsxβ Modal with category ratings, comment, image preview + upload flowEditReviewModal.tsxβ Edit modal that shows existing review images, allows marking them for removal, and adding new images (uploads to/api/review-uploadbefore saving)ImageCarousel.tsxβ Reusable carousel + lightbox used for review galleries and restaurant imagesCloudImage.tsxβ Lightweight wrapper / optimized image helper used throughout (abstracts image rendering)ReviewList.tsx/ReviewDetailModal.tsxβ Use the carousel to display review images
Integration notes:
AddReviewModalandEditReviewModalcreate client-side previews usingURL.createObjectURLand revoke them on close.- Both modals upload selected images to
/api/review-upload(data URL) and only store returnedsecure_urlvalues in the review document. ImageCarouselprovides thumbnails, next/prev controls, and a fullscreen lightbox.
Provide these in .env.local (example names used in code):
MONGODB_URIβ MongoDB connection string (required)NEXTAUTH_SECRETβ NextAuth secret for JWT signing (recommended)NEXTAUTH_URLβ App base URL (e.g. http://localhost:3000) (recommended)
Google OAuth 2.0 (required for "Sign in with Google"):
GOOGLE_CLIENT_IDβ Your Google OAuth 2.0 Client IDGOOGLE_CLIENT_SECRETβ Your Google OAuth 2.0 Client Secret
Cloudinary (one of the following sets must be present for uploads to work):
CLOUDINARY_URLβ Optional full connection URL ORCLOUDINARY_CLOUD_NAMECLOUDINARY_API_KEYCLOUDINARY_API_SECRET
Optional tuning vars used in lib/mongodb.js:
MONGO_SERVER_SELECTION_TIMEOUT_MS,MONGO_SOCKET_TIMEOUT_MS,MONGO_MAX_POOL_SIZE,MONGO_MIN_POOL_SIZE,MONGO_HEARTBEAT_MS,MONGODB_DIRECT_FALLBACK
- Install dependencies
npm install- Add
.env.localwith at least the required variables above. Example minimal.env.localfor local testing (replace placeholders):
MONGODB_URI=mongodb+srv://<user>:<pass>@cluster0.example.mongodb.net/bitecheck
NEXTAUTH_SECRET=some-long-secret
NEXTAUTH_URL=http://localhost:3000
# Google OAuth 2.0
GOOGLE_CLIENT_ID=your-google-client-id.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=your-google-client-secret
# Cloudinary example (pick either CLOUDINARY_URL or the explicit keys)
CLOUDINARY_URL=cloudinary://<api_key>:<api_secret>@<cloud_name>
- Go to Google Cloud Console
- Create a new project or select an existing one
- Navigate to "APIs & Services" β "Credentials"
- Click "Create Credentials" β "OAuth 2.0 Client ID"
- Configure the OAuth consent screen if prompted
- Choose "Web application" as the application type
- Add authorized redirect URIs:
- For local development:
http://localhost:3000/api/auth/callback/google - For production:
https://yourdomain.com/api/auth/callback/google
- For local development:
- Copy the Client ID and Client Secret to your
.env.localfile
- Run dev server
npm run dev- Test upload endpoint (optional quick check)
Use a client or curl to POST a small data URL to /api/review-upload. The app includes a debug GET on that endpoint that returns whether Cloudinary credentials are visible to the running process.
Example (browser / fetch) for manual test:
constdataUrl='data:image/png;base64,iVBORw0K...';constres=awaitfetch('/api/review-upload',{method: 'POST',headers: {'Content-Type': 'application/json'},body: JSON.stringify({file: dataUrl})});constjson=awaitres.json();console.log(json);- Add a review with images via the UI (Explore β Restaurant β Add Review) β images will be uploaded to Cloudinary then stored on the review document.
- The code includes defensive indexing and connection tuning in
lib/mongodb.jsfor more stable connections in constrained environments. - The
reviewsAPI attempts to clean up Cloudinary resources when deleting reviews. However, Cloudinary deletion relies on parsingpublic_idfrom stored URLs β edge cases may need improving. - Consider adding server-side validation for max images per review and rate-limiting for the upload endpoint.
- If you want, I can:
- Add an OpenAPI/Swagger summary for the API routes
- Add unit tests for API behavior (happy path + auth checks)
- Add a short troubleshooting section for common Cloudinary and MongoDB errors
If you want any section expanded (examples, diagrams, or an OpenAPI spec), tell me which part and I will add it.