Gary Jiang


Full Stack Developer w/ a bg in Growth Marketing 📈 + Creative 🎨

Pacer: MERN Stack Social Platform

Full-stack MERN platform for finding exercise partners for your favorite sports

Express React Redux Nodejs

Table of Contents

Idea

“I hate running alone but my friends run way too fast. I wish I had my own personal Pacer…”

In the world of running pacers play a crucial role by setting a consistent speed to help runners achieve their target times in a race.

The Pacer platform helps you connect with people playing your favorite sport at similar performance levels so you can train, bond, and celebrate together.

👉 Live Demo 👈


Features

  1. User Auth
  2. Events (CRUD) w/ Interactive Google Map
  3. Google Maps API Integration (Dynamic Map, Static Map, & Places API)
  4. User Profiles
  5. Discover Events Page
  6. Event Social Stats
  7. Event Comments (CRUD)

Plan

  1. Audience Value Props

    Audience-Value-Props

  2. App Architecture & Data Flow

    Data-Architecture-Flow

  3. Design

    Design-1-Discover
    Design-2-EventShow



Tech Stack

  • MongoDB
  • ExpressJS
  • React.js
  • Node.js

Notable Highlights

User-Auth
User-Auth-Sign-Up

User Authentication Modals

Sports-Ranking

Event Filter

Google-Map

Google Maps API Integration

Implementation

Data Architecture with Mongoose & MongoDB

Mongoose Models: Events, Users, Comments

const mongoose = require('mongoose');
const Schema = mongoose.Schema;

const commentSchema = new Schema({
  owner: {
    type: Schema.Types.ObjectId,
    ref: 'User',
    required: true
  },
  event: {
    type: Schema.Types.ObjectId,
    ref: 'Event',
    required: true
  },
  body: {
    type: String,
    required: true
  }
}, {
  timestamps: true
});

module.exports = mongoose.model('Comment', commentSchema);

Validations to ensure data integrity:

const validateEventInput = [
    check('owner')
        .exists({ checkFalsy: true })
        .withMessage('User must be logged in to create an event'),
    check('eventName')
        .exists({ checkFalsy: true })
        .isLength({ min: 5, max: 100 })
        .withMessage('Event must have a name between 5 and 100 characters'),
    check('description')
        .exists({ checkFalsy: true })
        .isLength({ min: 5, max: 1000 })
        .withMessage('Event must have a description between 5 and 1000 characters'),
    check('locationName')
        .exists({ checkFalsy: true })
        .withMessage('Event location must have a name'),
        ...

Google Maps API Integration

Integrated three Google Maps API endpoints:

  1. Dynamic interactive map on Events page.
  2. Static map preview on Discover page.
  3. Places API autocomplete search results when selecting an Event location.

Places-Autocomplete

<GoogleMap
  mapContainerStyle={mapStyles}
  zoom={14}
  center={{
    lat: selectedEvent?.latitude,
    lng: selectedEvent?.longitude,
  }}
>
  <Marker 
    position={{
    lat: selectedEvent?.latitude,
    lng: selectedEvent?.longitude,
    }}
    onClick={handleMarkerClick}
  />
  {infoWindowVisible && (
    <InfoWindow
      position={{
        lat: selectedEvent?.latitude,
        lng: selectedEvent?.longitude,
      }}
      onCloseClick={() => setInfoWindowVisible(false)}
    >
      <div>
        <p>Location: {selectedEvent?.locationName}</p>
        <p>Time of Event: {formattedTime}</p>
      </div>
    </InfoWindow>
  )}
</GoogleMap>

Express.js Routing

  • Three RESTful Express routes for Users, Events, Comments
  • Full CRUD for Events and Comments
    
router.post('/', requireUser, validateCommentInput, async (req, res, next) => {
  try {
    const newComment = new Comment({
      owner: req.body.owner,
      event: req.body.event,
      body: req.body.body
    });

    let comment = await newComment.save();

    comment = await Comment.findById(comment._id)
      .populate('owner', '_id firstName lastName profilePhotoUrl')
      .populate('event', '_id eventName locationName dateTime difficulty eventType maxGroupSize longitude latitude');

    return res.json(comment);
  } catch (err) {
    next(err);
  }
}); 

React.js Components

  • Discover Page:

    • Primary user interface with comprehensive Event filtering feature.
    • Displays all Events available for Users.
    • Event POST feature to create new Events.
    • Session and User validation required to create new Events.
      Discover-Page
  • Event Page

    • Displays Event details from Redux state.
    • Attending & Interested buttons with integration to update Redux and MongoDB.
    • Comments POST feature to create new Comments.
      Event-Page

State Management with Redux

  • Redux implemented for state management enhancing UI load times and reducing unnecessary API calls.
  • Redux slices of state for Users, Events, Comments.
  • Events reducer:
const eventsReducer = (state = { all: {}, user: {}, new: undefined }, action) => {
  switch (action.type) {
    case RECEIVE_EVENTS:
      const allEventsArray = action.events;

      const allEventsObject = allEventsArray.reduce((accumulator, event) => {
        accumulator[event._id] = event;
        return accumulator;
      }, {});

      return { ...state, all: allEventsObject, new: undefined };
    case RECEIVE_NEW_EVENT:
      return { ...state, all: { ...state.all, [action.event._id]: action.event }, new: undefined };
    case UPDATE_EVENT:
      return { ...state, all: { ...state.all, [action.event._id]: action.event }, new: undefined };
    case DELETE_EVENT:
      const { [action.eventId]: deletedEvent, ...rest } = state.all;
      return { ...state, all: rest, new: undefined };
    case RECEIVE_EVENT:
      return { ...state, all: { ...state.all, [action.event._id]: action.event }, new: undefined };
    case RECEIVE_USER_LOGOUT:
      return { ...state, user: {}, new: undefined }
    default:
      return state;
  }
};



Challenges

  1. Three days to learn the MERN stack

    • Team was well-versed with React and Node.js however Mongoose, MongoDB, and Express were new technologies for the team.
    • The minimalism of Mongoose, and Express created a forgiving and flexible application architecture.
    • Primary learning challenges were Express for backend routes due to it’s flexible nature vs. the team’s more familiar Rails convention over configuration paradigm.
  2. Google Maps and parsing location data

    • Implementing three different Google Maps API end points proved to be challenging.
    • (1) Dynamic Interactive Map, (2) Static map image, and (3) Places API search autocomplete suggestions
    • Each React component required different location data parsing logic to ensure UI loading efficiency across the user experience.
  3. Handling bugs

    • Debugging helped us understand the stack better via tracing bugs throughout the application.
    • Developed a three-tier approach to assess bugs efficiently across a full-stack application: (1) Backend logs (2) Frontend console logs (3) Middleware logic assessment



Team

Gary Jiang

Team & Frontend Lead
Github
Website
LinkedIn

Jason Jun

Flex Lead
Github
Website
LinkedIn

Francis Cawog

Backend Lead
Github
Website
LinkedIn

Robert Lee

Flex
Github
Website
LinkedIn