Project 2- Full-Stack Web & Data :
Cart + Account w/ MongoDB and Cloud Deployment

This is Group work

Start: Oct. 2

Due: Nov 13 @ START OF CLASS

Points: 170 points (see rubric on Canvas for Evaluation GUIDELINES ---NOTE THERE WILL BE PEER REVIEWS FOR THIS EVALUATION) + 20 points for Peer Review (to be discussed later)

******Peer Evaluated--how to do this******* (rubric on Canvas)

 

 

 

Project Details

 

 

 

 

 

This is a group project. In Project 1, you developed a client-side React Single Page Application (SPA) using static product data stored in a local products.json file.In Project 2, your group will select one member's Project 1 ad will extend that application into a full-stack web application by adding:

  • Node.js
  • Express
  • MongoDB Atlas
  • User accounts
  • Shopping cart persistence
  • Order processing
  • Cloud deployment

Your React application from Project 1 should continue to serve as the front end. Instead of loading data from a local JSON file, it will communicate with your Express backend using HTTP requests. As with Project 1 design is important. You will have all of the previous views from last time but, now the forms in them will function with your Node+Express Backend.

 

Project 2 Architecture

React Front End
      │
fetch() / HTTP
      │
     ▼
Node.js + Express REST API
                        │
┌────────┼────────┐
▼                   ▼                  ▼
Users         Product    Orders
                      │
                     ▼
              MongoDB Atlas

 

 

 

The additional functionality includes:

  • Create Account View - The Create Account View sends a POST request to the Express backend which will dynamically recieve the form information (after client-side validaton) and this backend code should:
    • perform server-side validate all information given is good
    • check to see if the login or email provided does not currently exist in (the users collection) in your MongoDB database and if so respond appropriatelt
    • if the login and email are unique then create a new user document in the users collection of your ModgoDB database and respond appropriately--specifically ask them to log in. Note: I will not ask you to verify the email address)
    • Add password security - Passwords must never be stored as plain text. The Express backend must hash passwords using an appropriate password-hashing library such as bcrypt before storing them in MongoDB. Login must compare the submitted password with the stored password hash.

  • Account View (prior to login)- Contains a form to ask for login/password and a link to Create Account View. If user logging in this view sends a POST request to the Express backend which will lookup the login/password in the MogoDB database and if valid or not. valid respond appropriately --if valid will go to Account View (logged in)
  • Account View (logged in) - this is displayed after the user logs in successfully and gives a response that echo's the user's basic information and provides a "dashboard" experience where the user can edit their account info as well as an optoin to view orders and to log out. Editing their account information should include all information except their unique identifier in MongoDB (The database should identify the user by MongoDB’s permanent _id, not by the email address.)

    ----------------------------------
    Welcome, Ben!
    ----------------------------------

    Username:
    bshah

    Email:
    ben@email.com

    Shipping Address

    123 Main Street
    Hayward, CA 94542

    Phone
    (510) 555-1212

    -------------------------------

    [ Edit Account ]

    [ View Orders ]

    [ Logout ]


 

  • Create a MongoDB database on Atlas with Google with minimallyCollections called : products, users and orders. A word about orders -they are processed through a series of forms launched from the Cart View. Credit card information will be collection. However, store only the last four digits of the card and some basic payment information. The merchant typically never stores the customer's credit card number. Instead, a payment processor (such as Stripe, Square, PayPal, or Authorize.net) handles it and returns a payment token or transaction ID. As you are not a real company and do not have accounts with Stripe, PayPal, etc. we are not actually processing payment information. For security reasons, do not store complete credit card numbers or CVV security codes in MongoDB. Your checkout form may collect this information to simulate an online purchase, but only the card type and last four digits (or no payment information at all) should be stored with the order.
    >>Note: you no longer have a products.json (from Project 1) in the code, everything comes from the database

    Example product document in products collection

     

     

    {
     "_id": "67c145fa1234567890abcdef",
     "sku": "SHOE-001",
     "name": "TrailRunner X2",
     "brand": "Peak Performance",
     "category": "Running Shoes",
     "price": 89.99,
     "salePrice": 79.99,
     "currency": "USD",
     "description": "Lightweight road running shoe designed for everyday training.",
     "longDescription": "The TrailRunner X2 combines a breathable engineered mesh upper 
      with a lightweight foam midsole to provide comfort during daily training runs. 
      A durable rubber outsole provides traction on pavement and light trails.",
     "gender": "Women's",
     "colors": [
       "Red",
       "White",
       "Blue"
                         ],
     "sizes": [
                         6,
                         6.5,
                         7,
                         7.5,
                         8,
                         8.5,
                         9,
                         9.5,
                         10
                         ],
     "weight": "9.4 oz",
     "material": "Engineered Mesh",
     "heelDrop": "8 mm",
     "archSupport": "Neutral",
     "waterResistant": false,
     "quantityInStock": 37,
     "rating": 4.7,
     "numberOfReviews": 183,
     "freeShipping": true,
     "featuredProduct": true,
     "newArrival": false,
     "image": "trailrunner-x2.jpg",
     "thumbnail": "trailrunner-x2-thumb.jpg"
                       }
    Example user document in users collection



    {
    "_id": "67c123abc456",
    "email": "oldemail@example.com",
    "passwordHash": "...",
    "firstName": "Ben",
    "lastName": "Shah",
    "phone": "510-555-1212",
    "shippingAddress": {
    "street": "123 Main Street",
    "city": "Hayward",
    "state": "CA",
    "zip": "94542"
    }
    }

    Order info:

     

    Order

    ├── Customer Information
    ├── Order ID & Date
    ├── Shipping Address
    ├── Payment Information
    ├── Items[]
    │ │
    │ ├── TrailRunner X2
    │ ├── Mountain Hiker Pro
    │ └── CityWalker Lite

    ├── Subtotal
    ├── Tax
    ├── Shipping
    └── Total

     

     

    Example order json

     

    {
     "_id": "ORD-1001",
     "userId": "67c123abc456",
     "orderDate": "2027-04-10T14:32:18Z",
     "customer": {
       "firstName": "Ben",
       "lastName": "Shah",
       "email": "ben@example.com",
       "phone": "510-555-1212"
                         },
     "shippingAddress": {
       "street": "123 Main Street",
       "city": "Hayward",
       "state": "CA",
       "zip": "94542"
                         },
     "payment": {
       "cardType": "Visa",
       "last4": "1234"
                         },
     "items": [
     {
       "productId": 1,
       "name": "TrailRunner X2",
       "brand": "Peak Performance",
       "size": 9,
       "color": "Red",
       "quantity": 2,
       "unitPrice": 79.99,
       "lineTotal": 159.98
                         },
     {
       "productId": 8,
       "name": "Mountain Hiker Pro",
       "brand": "Peak Performance",
       "size": 10,
       "color": "Brown",
       "quantity": 1,
       "unitPrice": 149.99,
       "lineTotal": 149.99
                         },
     {
       "productId": 15,
       "name": "CityWalker Lite",
       "brand": "Peak Performance",
       "size": 9.5,
       "color": "Black",
       "quantity": 1,
       "unitPrice": 99.99,
       "lineTotal": 99.99
                         }
     ],
     "subtotal": 409.96,
       "tax": 38.95,
       "shippingCost": 0.00,
       "total": 448.91,
     "status": "Submitted"
                       }

 

  • Products - ProductList + ProductCard with Backend

    The React ProductList and ProductCard components created in Project 1 will continue to be used.

    In Project 2, product information should be retrieved from the Express backend rather than directly from the local products.json file.

    The Express backend must provide product information to the React application as JSON and the general flow should be:

    React ProductList
      │
      │ GET request
     ▼
    Express REST API
      │
     ▼
    MongoDB products collection




  • Cart

    When the user selects a product, size, color, and quantity and clicks Add to Cart, the React application should add the selected item to the shopping cart.

    Each shopping-cart item should include at least:

    Product ID
    Product name
    Product image
    Selected size
    Selected color
    Quantity
    Unit price

    The navigation bar should display the total number of items currently in the cart.

    The shopping cart must persist during the user’s visit.

     



  • Cart + Session Variables: use Express session variables,

    The Express session stores the authoritative copy of the shopping cart. React synchronizes its displayed cart using HTTP requests.

    React state manages the displayed cart; the Express session manages the server-side cart for the visit.

    It does not conflict with React useState(). They serve different purposes:
    • React useState()
      = keeps cart data in the browser while the page is running
    • Express session
      = keeps cart data on the server for that browser session


    Recommended design:
  • React Cart State
       │
       │ fetch()
      ▼
    Express Session Cart
       │
      ▼
    Saved for the current user/browser session



    So React still needs:
    const [cartItems, setCartItems] = useState([]);

    When the application first loads:


    React sends GET /api/cart
      │
     ▼
    Express reads req.session.cart
      │
     ▼
    Express returns cart as JSON
      │
     ▼
    React stores it in useState()

    When the user adds a product

     

    ProductCard
      │
     ▼
    POST /api/cart
      │
     ▼
    Express updates req.session.cart
      │
     ▼
    Express returns updated cart
      │
     ▼
    React updates cartItems state

    WHY both React useState & Express Sessions:

    React useState() is needed so the interface updates immediately:

    • cart count changes,
    • Cart View rerenders,
    • subtotal changes,
    • quantities update.

    The Express session is needed so the cart survives:

    • navigation between views,
    • browser refreshes,
    • multiple API requests,
    • temporary server-side storage before checkout.


  • Order Confirmation Information

    Order ID

    Order Date

    Customer name and email
    Shipping address
    Billing address
    Card type and last four digits
    Each ordered item
       Selected size and color
       Quantity
       Unit price
       Line total
       Subtotal
    Tax
    Shipping
    Final total



  • Filestructure. Now you are adding a backend your structure will look something like:
    where the choosen Project 1 code will now be contained under client and the NodeJS+Express will be stored in server. Depending on your choice of IDE the exact organization may vary,
    but separation between React client and Express server must be clear (and I want top level client and server directories)

    project-root/

    ├── client/
    │ ├── src/
    │ │ ├── components/
    │ │ ├── views/
    │ │ ├── App.jsx
    │ │ └── main.jsx
    │ ├── public/
    │ ├── package.json
    │ └── vite.config.js

    ├── server/
    │ ├── controllers/
    │ │ ├── accountController.js
    │ │ ├── productController.js
    │ │ ├── cartController.js
    │ │ └── orderController.js
    │ │
    │ ├── routes/
    │ │ ├── accountRoutes.js
    │ │ ├── productRoutes.js
    │ │ ├── cartRoutes.js
    │ │ └── orderRoutes.js
    │ │
    │ ├── middleware/
    │ ├── app.js
    │ └── package.json

    ├── README.md
    └── .gitignore




  • Required Express API

    GET /api/products
    GET /api/products/:id

    POST /api/accounts
    POST /api/login
    POST /api/logout
    GET /api/account
    PUT /api/account

    GET /api/cart
    POST /api/cart
    PUT /api/cart/:itemId
    DELETE /api/cart/:itemId

    POST /api/orders
    GET /api/orders
    GET /api/orders/:id

     

    GET /api/products

    Returns the complete collection of products stored in the MongoDB products collection.

    The React Shop View uses this endpoint to retrieve all available products and display them using the ProductList and ProductCard components.


    GET /api/products/:id

    Returns the information for one specific product.

    The React Product Detail View uses this endpoint to retrieve detailed information about the selected product before displaying it.


    POST /api/accounts

    Creates a new user account.

    The Express backend must:

    • validate all submitted information
    • verify that the email address is unique
    • hash the password before storing it
    • create a new document in the users collection
    • return an appropriate success or error response

    POST /api/login

    Authenticates a user.

    The backend verifies the submitted email and password. If authentication succeeds, an Express session should be created that stores the user's MongoDB _id.

    >>Login must compare the submitted password with the stored password hash.

    >>After successful login, the Express backend should create a session containing the user’s MongoDB _id. (Session --> userId: MongoDB user _id)


    POST /api/logout

    Logs the current user out of the application.

    The Express session should be destroyed, and the user should be returned to the Home or Login view.


    GET /api/account

    Returns the account information for the currently logged-in user.

    The React Account View uses this endpoint to display the user's profile, shipping address, and other account information.


    PUT /api/account

    Updates the currently logged-in user's account information.

    The backend should validate the submitted data and verify that any new email address is unique before updating the corresponding document in the users collection.


    GET /api/cart

    Returns the current shopping cart stored in the Express session.

    The React application uses this endpoint when it loads or refreshes to synchronize its cart state with the server.


    POST /api/cart

    Adds a product to the shopping cart.

    The backend should validate the request, add the selected product (including selected options such as size and color) to the Express session cart, and return the updated shopping cart.


    PUT /api/cart/:itemId

    Updates an existing item in the shopping cart.

    Typical updates include increasing or decreasing the quantity of a product.

    The backend should validate the updated quantity before saving the modified cart.


    DELETE /api/cart/:itemId

    Removes one item from the shopping cart.

    After removing the item, the backend should return the updated shopping cart.


    POST /api/orders

    Creates a new order.

    The backend must:

    • verify that the shopping cart is not empty
    • retrieve the current user information
    • validate all checkout information
    • calculate the order totals
    • create a new order document in the MongoDB orders collection
    • clear the Express session shopping cart
    • return an appropriate success response

    The React application should then display an Order Confirmation View.

    GET /api/orders

     

    returns only the current user's orders



  • Express Controllers.
    Organize Express controller functions by responsibility. At minimum, provide controllers for accounts, products, shopping cart operations, and orders.
    Each controller should receive an HTTP request, perform required validation and database operations, and return an appropriate JSON response. (Controllers should NOT contain HTML. Controllers return JSON responses.)

    accountController.js
    productController.js
    cartController.js
    orderController.js





  • Server Side Validation

    The Express backend must validate all incoming data. Client-side React validation alone is not sufficient.

    Server-side validation should verify, as appropriate:


    Required fields are present
    Email has an appropriate format
    Email is unique when creating or editing an account
    Quantities are valid positive integers
    Products exist
    Submitted product prices are not trusted but looked up & cart total recalculated DO NOT just take submitted cart info
    The cart is not empty
    Addresses contain required fields
    Order totals are calculated by the server





  • Connecting to Database

    When open connection?
    Establish the MongoDB connection when the Express application starts and reuse that connection for incoming requests. Do not repeatedly open and close a new database connection inside every controller function. You should add this logic to your async function startServer() in the server.js file. You will add the a connection url to the environment variables in your Google Cloud Run configuration like the following. MONGODB_URI=mongodb+srv://yourLogin:yoiurPassword@cluster0.mongodb.net/StoreDB that you can access via process.env.MONGODB_URI in your startServer() function. The URI is specific to your MongoDB collection and this is just an example.

    In the Google Cloud Console:

    Cloud Run

    YOUR Deployed Service Name

    Edit & Deploy New Revision





    Scroll to Environment Variables & add new variable with URI

     

    >>>Alternatively you can choose to use Google Secrets Manager (and this is for you to learn on your own)

     

    >>>During development, store your MongoDB connection string in a .env file (NEVER push to GitHub). When deploying to Google Cloud Run, configure MONGODB_URI as an environment variable in the Cloud Run service.

     

     

    Each Cloud Run instance creates and reuses its own MongoDB connection pool.



    Express starts

    Open MongoDB connection

    Request

    Use connection

    Request

    Use connection

    Request

    Use connection

     

     

    So your Express Application consists of:

    ----------------------------

    Routes

    Controllers

    Sessions

    Database Connection

    ----------------------------

     

     

     

    When close connection?

    when the Express Application is stopped (either as and admin you stop it or for some reason Google Cloud Run stops it) you should clean up and close the connection.

     

     

    What is happening when Google Cloud Run creates mutiple instances of Express Application?

    Note Google Cloud Run can scale and create multiple Express application instances ---in this case each Cloud Run instance creates and reuses its own MongoDB connection pool.






  • Error Handling - your code must appropriately handle errors / exceptions


  • Code Quality and Style

    Your project should be written using professional software engineering practices.

    General Requirements:
    • Code should be sufficiently commented.
    • Use meaningful, descriptive names for variables, functions, components, classes, files, and directories.
    • Follow a consistent naming convention throughout the project.
    • Avoid duplicated code whenever possible by creating reusable React components and helper functions.
    • Keep individual functions reasonably short and focused on one responsibility.
    • Organize code appropriately (See above)
    • Maintain consistent formatting throughout the project. (consider lower Camel case or similar)


  • Deployment - both front-end and back-end to Google Cloud Run & related code development. DEPLOYMENT GUIDE

    Deploy the complete full-stack application to Google Cloud Run as one service. Before deployment, build the React application using Vite. The Express server must serve the generated React static files and provide the required /api endpoints. The deployed Cloud Run service will therefore host both the React front end and the Express backend. MongoDB Atlas will remain the external database.


    You need to make sure that the Express application checks in order:
    1. Express checks /api routes
    2. Express checks for static React files
    3. Other requests receive React's index.html


    So: GET /api/products is handled by Express while GET /shop is handled by React's index.html after which React displays the Shop View.


    One URL for everything

    The deployed application could have one URL like the following https://cs351-store-xxxxx.run.app
    >>>> React loads from https://cs351-store-xxxxx.run.app/

    >>>> The API is available from the same service for example https://cs351-store-xxxxx.run.app/api/products

    >>>> React can therefore use relative URLs: const response = await fetch("/api/products");

    NOTE: while we could deploy the front-end and back-end seperately and this could be done in industry (especially if different teams maintaining) I am making it simpler for you



GITHUB

  1. Create a new group Github repository and share your NodeJS+Express project to this repository
  2. Have README.md that must include Project overview, Group members, Build instructions,Deployment instructions
  3. Create a docs directory and publish to Github pages documentation to teach others about your project and code in the repository.
  4. PROJECT MANAGEMENT: Create a Issues Board for your Github respository and for EVERY ITEM listed above under project details you are to create MULTIPLE cards per item (there are multiple pieces to most of the items listed above). Assign people (not everyone to every card) to the cards AND set due dates for each card. During every group meeting working on this project you need to update your issues board.
  5. Create a Wiki in your Github repository that contains
  • home page = link to the website
  • create Account demo = video showing the before and after of the MongoDB AND a user using the website to create a new account.
  • shopping + viewcart = video showing the shopping cart demo where the user goes to different product pages, adds them to a cart and at different times during shopping views the cart which shows a listing of all of the current items in the cart (view cart)
  • checkout demo= show the before and after screenshots of any forms used to collect user information of address and billing as well as the before and after of the conent in the orders collection.

 

 

 


  Deliverables: 

 


  • November 13 @ START OF CLASS

     

    • Turn in BOTH URL to the Github AND the URL to the Google Cloud Run app to Post to Canvas->Assignments->Project 2

      WILL Present work Nov 13 in class (continuing as necessary next class) + Peer Reviews due Nov. 20



      Regarding peer reviews, the instructor will review the peer reviews, if appropriate the group will maintain the average of the peer reviews given (only a subset of students will review your work) ---however, the instructor WILL alter the review score as necessary to reflect properly the work performed (and this can be to increase OR descrease the score). Also, points are awarded for doing peer reviews and if improper reviews are done or poor quality reviews are done, a student doing so will loose those points.
      ******Peer Evaluated--how to do this******* (rubric on Canvas)