E-Commerce Website

React, Redux, MongoDB, Node, Express, Bootstrap
WARNING ABOUT LOADING TIME!
Be aware that this project is hosted with a free tier service. This means that it will take a while for the server to spin up when it's accessed. Thank you for your patience!

Description

Fully functional fullstack e-commerce website.

  • State management with Redux.
  • Route and controller logic with JWT authentication.
  • Admin area with CRUD functionality for users, products and orders.
  • MongoDB.
  • Pages including: product listing, product detail page with reviews, user login, payment with Paypal, shipment, etc.


Frontend Diagram



Store

Combines all reducers and apply thunk (middleware that allows you to return functions).

View code
1const reducer = combineReducers({
2  productList: productListReducer,
3  //followed by all other reducers...
4});
5
6const cartItemsFromStorage = localStorage.getItem('cartItems')
7  ? JSON.parse(localStorage.getItem('cartItems'))
8  : [];
9
10//followed by other localStorage variables...
11
12const initialState = {
13  cart: {
14    cartItems: cartItemsFromStorage,
15    shippingAddress: shippingAddressFromStorage,
16  },
17  userLogin: { userInfo: userInfoFromStorage },
18};
19
20const middleware = [thunk];
21
22const store = createStore(
23  reducer,
24  initialState,
25  composeWithDevTools(applyMiddleware(...middleware))
26);
27
28export default store;
29

Thunk

Redux Thunk is middleware that allows you to return functions, rather than just actions, within Redux. This allows for delayed actions, including working with promises. One of the main use cases for this middleware is handling asynchronous actions like using axios to send a GET request as seen in the Actions API call.

Constants

Just holds the constants names. The cases are: REQUEST, SUCCESS, FAIL, RESET.

Actions

Using async try/catch it fetches APIs and dispatches a type (e.g. REQUEST, SUCCESS, FAIL, RESET) that hits a Switch Case in the Reducer.

View code
1export const listProducts =
2  (keyword = '', pageNumber = '') =>
3  async (dispatch) => {
4    try {
5      dispatch({
6        type: PRODUCT_LIST_REQUEST,
7      });
8
9      const { data } = await axios.get(
10        `/api/products?keyword=${keyword}&pageNumber=${pageNumber}`
11      );
12
13      dispatch({
14        type: PRODUCT_LIST_SUCCESS,
15        payload: data,
16      });
17    } catch (error) {
18      dispatch({
19        type: PRODUCT_LIST_FAIL,
20        payload:
21          error.response && error.response.data.message
22            ? error.response.data.message
23            : error.message,
24      });
25    }
26  };
27

Reducers

Takes previous state and action with its Switch Case and returns next state.

View code
1export const productListReducer = (state = { products: [] }, action) => {
2  switch (action.type) {
3    case PRODUCT_LIST_REQUEST:
4      return { loading: true, products: [] };
5    case PRODUCT_LIST_SUCCESS:
6      return {
7        loading: false,
8        products: action.payload.products,
9        pages: action.payload.pages,
10        page: action.payload.page,
11      };
12    case PRODUCT_LIST_FAIL:
13      return { loading: false, error: action.payload };
14    default:
15      return state;
16  }
17};
18

Screens

useDispatch & useSelector are both Redux Hooks.
useDispatch will trigger the action and useSelector will receive the state.

Components

Some components also use Redux, like ProductCarousel and Header.



Backend Diagram




Server

The heart of the backend application. Connects to the DB via config/db and uses all routes made available by routes (order, product, user, upload) and error handling directly from the middleware.

Config/db

Makes the connection to the database.

Routes

Best regarded as Endpoints. Route methods (get, post, put, delete) are defined for the specified route and used in conjunction with a controller and middleware functions, which hold the logic. (e.g. router.post('/login', authUser))

Controllers

Best regarded as the application logic. The functions defined here will be requested when hitting the defined routes/endpoints. It is the place where the logic for a given route is applied.

Middleware

Either called directly from server.js for errorHandling or from a given route, in this case userRoute, for both protect and isAdmin functions. protect will make sure that the user is logged in by verifying their bearer token (which is generated by the util). isAdmin will make sure that the given user has admin rights.

Utils

Generates a token which is called from the userController. (e.g. when logging in a user)

Models

Defines the DB schema for a given model. userModal.js also uses bcrypt to compare and hash passwords.