Fruit and Vegetable Market Shop using MEAN

Last Updated : 23 Jul, 2025

In this comprehensive guide, we’ll walk through the step-by-step process of creating a Fruit and Vegetable Market Shop using the MEAN (MongoDB, Express.js, Angular, Node.js) stack. This project will showcase how to set up a full-stack web application where users can view, filter, and purchase various fruits and vegetables.

Screenshot-(198)-(1)-2-(1)-(1)-(1)
Final Project Output

Prerequisites:

Approach

The project will be divided into two main parts:

Client-side: The client-side will be built using Angular, a popular JavaScript framework for building web applications. We’ll create the necessary components, services, and routing to handle the user interface and interact with the server-side.

Server-side: The server-side will be built using Node.js and Express, a web application framework for Node.js. We’ll create the API endpoints, handle database operations using Mongoose, and manage Cross-Origin Resource Sharing (CORS) policies.

Steps to Create the Project

Step 1: Create a root directory(MEAN) for your project

mkdir Fruits
cd Fruits

Step 2: Create a root directory for your server-side project.

mkdir server
cd server

Step 3: Initialize a new Node.js project

npm init -y

Step 4: Install the required dependencies

npm install cors dotenv express mongoose 

Project Structure (Backend):


Screenshot-(285)
Backend Structure

Updated dependencies will look like:

{
"name": "server",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"description": "",
"dependencies": {
"cors": "^2.8.5",
"express": "^4.19.2",
"mongoose": "^8.4.0"
}
}

Example: To demonstrate creating the server endpoint which is consumed by the front-end application.

JavaScript
//index.js

const express = require("express");
const mongoose = require("mongoose");
const cors = require("cors");

const app = express();
app.use(express.json());
app.use(cors());

// Connect to MongoDB
const MONGO_URL = `your db url`;

mongoose
  .connect(MONGO_URL)
  .then(() => {
    console.log("Connected to MongoDB");
    seedProductsData();
  })
  .catch((error) => {
    console.log("Failed to connect to MongoDB", error);
  });

const productsSchema = new mongoose.Schema({
  name: String,
  description: String,
  price: Number,
  type: String,
  image: String,
});
const ProductsModel = mongoose.model("Products", productsSchema);

async function seedProductsData() {
  await ProductsModel.deleteMany({});

  const productsData = [
    {
      name: "Apple",
      type: "Fruit",
      description: "Fresh and crispy",
      price: 150,
      image:
        "https://media.geeksforgeeks.org/wp-content/uploads/20240104142542/apple.jpg",
    },
    {
      name: "Banana",
      type: "Fruit",
      description: "Rich in potassium",
      price: 75,
      image:
        "https://media.geeksforgeeks.org/wp-content/uploads/20240104142554/banana.jpg",
    },
    {
      name: "Orange",
      type: "Fruit",
      description: "Packed with vitamin C",
      price: 200,
      image:
        "https://media.geeksforgeeks.org/wp-content/uploads/20240104142641/orange.jpg",
    },
    {
      name: "Carrot",
      type: "Vegetable",
      description: "Healthy and crunchy",
      price: 100,
      image:
        "https://media.geeksforgeeks.org/wp-content/uploads/20240104142613/carrot.jpg",
    },
    {
      name: "Broccoli",
      type: "Vegetable",
      description: "Nutrient-rich greens",
      price: 175,
      image:
        "https://media.geeksforgeeks.org/wp-content/uploads/20240104142601/brocoli.jpg",
    },
    {
      name: "Grapes",
      type: "Fruit",
      description: "Sweet and juicy",
      price: 250,
      image:
        "https://media.geeksforgeeks.org/wp-content/uploads/20240104142629/grapes.jpg",
    },
    {
      name: "Strawberry",
      type: "Fruit",
      description: "Delicious red berries",
      price: 300,
      image:
        "https://media.geeksforgeeks.org/wp-content/uploads/20240104142657/strawberry.jpg",
    },
    {
      name: "Lettuce",
      type: "Vegetable",
      description: "Crisp and fresh",
      price: 120,
      image:
        "https://media.geeksforgeeks.org/wp-content/uploads/20240104142635/lettue.jpg",
    },
    {
      name: "Tomato",
      type: "Vegetable",
      description: "Versatile and flavorful",
      price: 180,
      image:
        "https://media.geeksforgeeks.org/wp-content/uploads/20240104142704/tomato.jpg",
    },
    {
      name: "Cucumber",
      type: "Vegetable",
      description: "Cool and hydrating",
      price: 130,
      image:
        "https://media.geeksforgeeks.org/wp-content/uploads/20240104142621/cocumber.jpg",
    },
  ];

  await ProductsModel.insertMany(productsData);
  console.log("Products data seeded successfully!");
}

app.get("/api/products", async (req, res) => {
  try {
    const products = await ProductsModel.find({});
    res.status(200).json({ success: true, data: products });
  } catch (error) {
    res.status(500).json({ success: false, error: error.message });
  }
});

const PORT = process.env.PORT || 4000;
app.listen(PORT, () => {
  console.log(`Server is running on port ${PORT}`);
});

To start the server run the following command.

node index.js

Steps to create Front End

Step 1: Run the following command to install Angular CLI globally:

npm i @angular/cli@16.0.0-rc.0

Step 2: Go to Fruits directory and run this command

mkdir client

Step 3: Create Angular App

ng new fruit-vegetable-market-app

Updated dependencies will look like:

{
"name": "fruit-vegetable-market-app",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"watch": "ng build --watch --configuration development",
"test": "ng test"
},
"private": true,
"dependencies": {
"@angular/animations": "^17.3.0",
"@angular/common": "^17.3.0",
"@angular/compiler": "^17.3.0",
"@angular/core": "^17.3.0",
"@angular/forms": "^17.3.0",
"@angular/platform-browser": "^17.3.0",
"@angular/platform-browser-dynamic": "^17.3.0",
"@angular/router": "^17.3.0",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
"zone.js": "~0.14.3"
},
"devDependencies": {
"@angular-devkit/build-angular": "^17.3.7",
"@angular/cli": "^17.3.7",
"@angular/compiler-cli": "^17.3.0",
"@types/jasmine": "~5.1.0",
"jasmine-core": "~5.1.0",
"karma": "~6.4.0",
"karma-chrome-launcher": "~3.2.0",
"karma-coverage": "~2.2.0",
"karma-jasmine": "~5.1.0",
"karma-jasmine-html-reporter": "~2.1.0",
"typescript": "~5.4.2"
}
}

Folder Structure:

Screenshot-(283)
Front end structure

Stpes to create the front-end of the project

Step 1: Create navbar.component.html,navbar.component.ts inside navbar folder

Step 2: Create product-card.component.ts, product-card.component.html insider product-card folder

Step 3: Update code of styles.css and main.ts

Step 4: Update code of app.component.html , app.component.ts and app.component.css

HTML
<!-- app.component.html -->
<div class="container">
  <app-navbar></app-navbar>
  <div class="filters">
    <div class="rating-container">
      <label for="rating">Filter by Type:</label>
      <select id="rating" [formControl]="rating" (change)="filterRestaurants()">
        <option value="">All</option>
        <option value="Fruit">Fruit</option>
        <option value="Vegetable">Vegetable</option>
      </select>
    </div>
    <div>
      <button (click)="sortByPrice()">Sort by Price</button>&nbsp;
      <label for="minPrice">Min Price:</label>
      <input
        type="number"
        id="minPrice"
        [formControl]="minPrice"
        (change)="filterRestaurants()"
      />&nbsp;
      <label for="maxPrice">Max Price:</label>
      <input
        type="number"
        id="maxPrice"
        [formControl]="maxPrice"
        (change)="filterRestaurants()"
      />
      <button (click)="filterByRange()">Filter by Range</button>
    </div>
    <div>
      Search :
      <input
        type="text"
        [(ngModel)]="searchText"
        (keyup)="applySearch()"
        placeholder="Search..."
      />
    </div>
  </div>

  <div class="restaurants">
    <h3>Restaurants</h3>
    <span class="cart">
      Cart
      <div className="cart-content">
        <span style="color: brown">Total Price : $ {{ cartTotal }}</span>
      </div>
    </span>
    <div class="restaurant-cards">
      <div
        *ngFor="let restaurant of filteredRestaurants"
        class="restaurant-card"
      >
        <img [src]="restaurant.image" alt="{{ restaurant.name }}" />
        <div class="card-body">
          <h5 class="card-title">{{ restaurant.name }}</h5>
          <p class="card-text">
            {{ restaurant.description }}
          </p>
        </div>
        <ul class="list-group list-group-flush">
          <li class="list-group-item">Price: {{ restaurant.price }} Rs/Kg</li>
        </ul>
        <button class="btn1" (click)="addToCart(restaurant.price)">
          Add to Cart</button
        >{{ " " }}
        <button (click)="removeFromCart(restaurant.price)">-</button>
      </div>
    </div>
  </div>
</div>
HTML
<!-- navbar/navbar.component.html -->

<nav class="navbar navbar-expand-lg" style="background-color: green;">
    <div class="container" >
        <a class="navbar-brand" href="#"><img src={{title}}></a>
        Fruit And Vegetable Market using MEAN
        <div class="collapse navbar-collapse" id="navbarNav">
            <ul class="navbar-nav ms-auto">
            </ul>
        </div>
    </div>
</nav>
HTML
<!-- product-card/product-card.component.html -->

<div class="card">
    <img [src]="restaurant.image" 
    class="card-img-top" [alt]="restaurant.name">
    <div class="card-body">
        <h5 class="card-title">{{ restaurant.name }}</h5>
        <p class="card-text">
            Types of food we offer: {{ restaurant.cuisines.join(', ') }}
        </p>
    </div>
    <ul class="list-group list-group-flush">
        <li class="list-group-item">Address: {{ restaurant.address }}</li>
        <li class="list-group-item">City: {{ restaurant.location }}</li>
        <li class="list-group-item">Rating: {{ restaurant.rating }}</li>
    </ul>
</div>
HTML
<!-- index.html -->

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>ResturentApp</title>
  <base href="/">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" type="image/x-icon" href="favicon.ico">
  <!-- Latest compiled and minified CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://unpkg.com/bootstrap@5.3.3/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="https://unpkg.com/bs-brain@2.0.4/components/blogs/blog-2/assets/css/blog-2.css">
</head>
<body>
  <app-root></app-root>
</body>
</html>
CSS
/* app.component.css */

.headers {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-left: 5vw;
    margin-right: 5vw;
    margin-top: 3vh;
    padding-bottom: 2vh;
    border-bottom: 1px solid gray;
}

.location-container {
    width: 10vw;
}

.cuisines-container {
    margin-top: 5vh;
    width: 15vw;
    height: 10vh;
    overflow-y: scroll;
}

.cuisine-checkbox {
    display: flex;
    align-items: center;
    margin-bottom: 0.5rem;
}

.cuisine-checkbox input {
    margin-right: 0.5rem;
}

.rating-container {
    width: 10vw;
}

.restaurants {
    padding-bottom: 3vh;
}

.restaurants h3 {
    margin-left: 5vw;
    margin-top: 1vh;
    margin-bottom: 1vh;
}

.restaurant-cards {
    display: flex;
    flex-wrap: wrap;
    margin-left: 5vw;
}

.restaurant-card {
    width: 18rem;
    margin-left: 4vw;
    margin-top: 4vh;
}

.restaurant-card img {
    height: 20vh;
    background-size: cover;
}
.btn1{
    margin: 10px;
}
.cart{
     position: fixed;
    top: 250px;
    right: 10px;
    width: 200px;
    border: 2px solid #4CAF50;
    border-radius: 10px;
    height: 20vh;
    background-color: #fff;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
    overflow-y: auto;
    z-index: 1000;
}
.cart-content{
     padding: 16px;
}
CSS
/* You can add global styles to this file, and also import other style files */
/* styles.css */
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap');

body {
    font-family: 'Poppins', sans-serif;
    background-color: #f5f5f5;
    color: #333;
    margin: 0;
    padding: 0;
}

.container {
    max-width: 1600px;
    margin: 0 auto;
    padding: 1rem;
}

.navbar {
    font-size: 25px;
    background-color: #333;
    color: white;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.navbar-brand {
    font-size: 1.5rem;
    font-weight: 700;
    text-decoration: none;
}

.filters {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-top: 2rem;
    padding-bottom: 2rem;
    border-bottom: 1px solid #ddd;
}

.location-container,
.rating-container {
    width: 15%;
}

.cuisines-container {
    width: 30%;
    max-height: 200px;
    overflow-y: auto;
}

.cuisine-checkbox {
    display: flex;
    align-items: center;
    margin-bottom: 0.5rem;
}

.cuisine-checkbox input {
    margin-right: 0.5rem;
}

.restaurants {
    margin-top: 2rem;
}

.restaurants h3 {
    font-size: 1.75rem;
    font-weight: 600;
    margin-bottom: 1.5rem;
}

.restaurant-cards {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
    grid-gap: 2rem;
}

.restaurant-card {
    background-color: #fff;
    border-radius: 0.5rem;
    box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
    overflow: hidden;
    transition: transform 0.3s ease-in-out;
}

.restaurant-card:hover {
    transform: translateY(-5px);
    box-shadow: 0 8px 12px rgba(0, 0, 0, 0.15);
}

.restaurant-card img {
    height: 100%;
    width: 90%;
    /* object-fit: contain; */
    /* object-position: center; */
    margin-left: 15px;
}


.card-body {
    padding: 1.5rem;
}

.card-title {
    font-size: 1.25rem;
    font-weight: 600;
    margin-bottom: 0.5rem;
}

.card-text {
    font-size: 0.9rem;
    color: #666;
    margin-bottom: 1rem;
}

.list-group-item {
    font-size: 0.9rem;
    padding: 0.75rem 1.5rem;
    border-bottom: 1px solid #f1f1f1;
}

.list-group-item:last-child {
    border-bottom: none;
}
JavaScript
// appConfig.config.js

import { ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';

import { routes } from './app.routes';

export const appConfig: ApplicationConfig = {
  providers: [provideRouter(routes)]
};
JavaScript
// app.component.ts

import { NgModule } from '@angular/core';
import { Component, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { FormControl, ReactiveFormsModule } from '@angular/forms';
import { CommonModule } from '@angular/common';
import { NavbarComponent }
  from './components/navbar/navbar.component';
import { ResturentCardComponent }
  from './components/product-card/product-card.component';

import { FormsModule } from '@angular/forms';
import { Router } from '@angular/router';

  
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
  imports: [
    NavbarComponent,
    ResturentCardComponent,
    ReactiveFormsModule,
    CommonModule,
    FormsModule,
    
  ],
  standalone: true,
})
export class AppComponent implements OnInit {
  title = 'Restro - find your restaurant';
  rating = new FormControl('');
  restaurants: any[] = [];
  filteredRestaurants: any[] = [];
  searchResults: string[] = [];
  searchText: string = '';
  cartTotal: number = 0;
  minPrice = new FormControl('');
  maxPrice = new FormControl('');
  minFilterPrice: number | null = null;
  maxFilterPrice: number | null = null;

  constructor(private http: HttpClient, private router: Router) { }
  

  ngOnInit() {
    this.getRestaurants();
  }

  getRestaurants() {
    this.http.get('http://localhost:4000/api/products').subscribe(
      (response: any) => {
        if (response.success) {
          this.restaurants = response.data;
          this.filterRestaurants();
        }
      },
      (error) => {
        console.log('Error:', error);
      }
    );
  }

  filterRestaurants() {
    this.filteredRestaurants = this.restaurants.filter((restaurant) => {
      let passType = true;
      if (this.rating.value === 'Fruit') {
        passType = restaurant.type === 'Fruit';
      } else if (this.rating.value === 'Vegetable') {
        passType = restaurant.type === 'Vegetable';
      }

      let passPriceRange = true;
      if (this.minFilterPrice !== null) {
        passPriceRange = restaurant.price >= this.minFilterPrice;
      }
      if (this.maxFilterPrice !== null) {
        passPriceRange = passPriceRange && restaurant.price <= this.maxFilterPrice;
      }

      return passType && passPriceRange;
    });
  }
  
  filterByRange() {
    const minValue = this.minPrice.value;
    const maxValue = this.maxPrice.value;
    if (minValue !== null && maxValue !== null) {
      const minPrice = +minValue;
      const maxPrice = +maxValue;

      if (!isNaN(minPrice) && !isNaN(maxPrice) && maxPrice >= minPrice) {
        this.minFilterPrice = minPrice;
        this.maxFilterPrice = maxPrice;
        this.filterRestaurants();
      } else {
        alert("Invalid price range");
      }
    }
  }

  applySearch(): void {
    this.filteredRestaurants = this.restaurants.filter(restaurant =>
      restaurant.name.toLowerCase().includes(this.searchText.toLowerCase())
    );
  }

  addToCart(price: number): void {
    this.cartTotal += price;
  }

  removeFromCart(price: number): void {
    if (this.cartTotal > 0) {
      this.cartTotal -= price;
    } else if(this.cartTotal === 0) {
      // Optionally handle negative total or alert user
      alert("No Item")
      return;
    }
  }
  sortByPrice() {
    this.filteredRestaurants.sort((a, b) => a.price - b.price);
  }
  
}
JavaScript
// product-card/product-card.component.ts

import { Component, Input } from '@angular/core';

@Component({
    selector: 'app-resturent-card',
    templateUrl: './product-card.component.html',
    standalone: true
})
export class ResturentCardComponent {
    @Input() restaurant: any;
}
JavaScript
// navbar/navbar.component.ts

import { Component } from '@angular/core';

@Component({
    selector: 'app-navbar',
    templateUrl: './navbar.component.html',
    standalone: true
})
export class NavbarComponent {
    title = 'https://media.geeksforgeeks.org/gfg-gg-logo.svg';
}
JavaScript
// main.ts

import { bootstrapApplication } from '@angular/platform-browser';
import { importProvidersFrom } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';
import { AppComponent } from './app/app.component';

bootstrapApplication(AppComponent, {
    providers: [
        importProvidersFrom(HttpClientModule)
    ]
})
    .catch(err => console.error(err));

Start the Angular App using the following command.

ng serve

Output:

ResturentApp-Brave2024-05-1514-24-09-ezgifcom-optimize
Final Project Output
Comment

Explore