My Top Stop

Explore and share your top ride share destinations from the past year. Receive recommendations for similar destinations you might like.

🔥 Try it now at mytopstop.com!


Problems ➡️ Solutions




A





Problem (A): Pivot

I learned a valuable lesson to fully understand the abilities and limitations of an external API before building an app around it.

My initial idea for this project was to create a dashboard that informed a user of their Lyft ride’s estimated ride time vs. actual ride time.

Lyft does not save the estimated ride time, it is only accessible at the beginning of a ride. My workaround for this was to create a webhook on my API that would save the estimated ride time when a ride was started. I had saved authentication tokens for Lyft, so I assumed that once a user connects with my app, I could receive webhooks of their estimated ride time when they started a ride through the Lyft app…

Wrong.

I would only have access to this data if the user requested a ride through my app rather than the Lyft app. This drastically altered my concept for a passive app and I decided to rethink my approach.

Solution (A): Pivot

I landed on the idea of my current app, My Top Stop. With a user’s historic Lyft rides, I could still create some value in combining additional API’s - Google maps and Yelp.

Results (A): Pivot

I’m actually much happier with the new design of the app. My Top Stop is much more interactive and social than my original design. This was a good lesson in implementing your design due diligence before building.

•••••••••••••••••••


B





Problem (B): Multiple Middleware: Automation and Sequencing

The challenge here was to implement multiple different API calls through middleware when loading the app.

Solution (B): Multiple Middleware: Automation and Sequencing

Once you login through Lyft, what needs to happen?

  • Fetch the past year of Lyft rides
  • Sort your Lyft rides by most duplicates first, followed by most recent
  • Fetch three Yelp locations near your Lyft destination
  • Fetch recommendations based on your top three destinations

This all happens during the load screen:

There is a nice way to implement multiple middleware at once. This can be done by using redux-multi to create an action that dispatches an array of actions.

function initDashboard () {
  return [
    fetchLyftRides(),
    sortLyftRides(),
    fetchYelpLocations(),
    fetchYelpRecommendations()
  ]
}

This is a good start for automating multiple middleware scripts at once, however, we run into a new problem.

When calling the action initDashboard(), we will fire a dispatch for each action in the array. fetchLyftRides() will begin, and immediately after, sortLyftRides() will begin. While fetchLyftRides() is calling the Lyft API, the sortLyftRides() middleware will begin trying to sort the fetched rides. If fetchLyftRides() has not completed yet, we will return an incomplete, inaccurate sorted list of rides.

To avoid this problem using multiple middleware, we need to make sure they complete in order:

  1. Fetch the past year of Lyft rides
  2. Sort your Lyft rides by most duplicates first, followed by most recent
  3. Fetch three Yelp locations near your Lyft destination
  4. Fetch recommendations based on your top three destinations

In order to sequence my middleware, I have my middleware listen for when the previous middleware is successfully completed:

const recommendationMiddleware = store => next => action => {

  if (action.type === 'FETCH_YELP_SUCCESS') {
    if(!store.getState().fetcher.isFetchingRecommendations) {
      fetchResultsRequest(store, action);
    }
  }
  next(action);
  return action;
}

Okay, so this works for dispatching multiple middleware sequentially, but what if I want to call this middleware individually?

That is also possible by creating the middleware’s own action and also listen for it in the fetch logic:

const recommendationMiddleware = store => next => action => {

    if (action.type === 'FETCH_RECOMMENDATION_REQUEST' || action.type === 'FETCH_YELP_SUCCESS') {
      if(!store.getState().fetcher.isFetchingRecommendations) {
        fetchResultsRequest(store, action);
      }
    }
    next(action);
    return action;
  }

Results (B): Multiple Middleware: Automation and Sequencing

By implementing this form a sequencing through my middleware, I’m able to organize and separate my middleware, and at the same time, assure that they fetch in the correct order.


•••••••••••••••••••


C





Problem (C): Decoupled API & Client

The challenge here was to create a modern decoupled app - Rails backend and React/Redux frontend.

The difficulties that arose were:

  • 0Auth2
  • User login and security
  • Communicating params between client and API

Solution (C): Decoupled API & Client

0Auth2

When connecting users to their Lyft account, Lyft requires an 0Auth2 authentication. This is a common authentication method these days and can be implemented well with rails using Omniauth.

Some common services even have specialized Omniauth “strategies” bundled into gems such as Facebook.

Unfortunately, I could not find an open source Lyft Omniauth strategy. This forced me to create my own Omniauth strategy for Lyft.

This was a great learning experience in itself as Omniauth is a great middleware service to learn for Rails.

User Login and Security

When creating a Ruby on Rails app, it is very easy to implement a fairly secure login. This is because your frontend is directly connected with your backend, allowing the app to know your current session at all times.

However, when your client is decoupled from your backend, and you must access it via an API, you must persist a login user token on your client side, and check it against your backend each time you communicate with your API.

Thankfully, there are some good tools out there for this exact problem. I used these:

I was surprised how much work has to be done to secure communication between API’s and clients.

Communicating Params Between Client and API

There are times when you need to communicate information back and forth between your client and API without creating an individual API call.

A good way to implement this is through URL parameters. I use URL parameters, “params,” in two ways.

The first is by providing user identification params to my Lyft authentication callback. This allows me to match the correct user to the Lyft authentication token (In the my current build, this is deprecated).

The second is to communicate the Lyft authentication token to my client without saving it in my backend. I could increase security further by saving the Lyft token to my backend and masking it behind a separate ID. However, under the app’s current permissions, the provided Lyft token only provides ride history information and expires after one hour.

Using params is a fast, useful, and light weight way to move information.

Results (C): Decoupled API & Client

Having a decoupled API and Client is very valuable for scaling an app. In today’s environment, an app has to come out the box almost immediately supporting multiple clients - whether web, mobile, desktop, or more.

Starting out with a decoupled API and client allows for an easier implementation of multiple clients. However, if speed of initial go to market is required, it’s possible that decoupling is not the best route - that being said, decoupling will take more time later.

•••••••••••••••••••


Project Design


🔎 UX

I took a Job Story approach to my user experience.

👀 Visual

My first sketch laid out the material tiles.

Most of the app was built with simple buttons before adding any styling.

The original design had a persisted “Connect Lyft” button once the user connected.

The final design provides a much clearer connection flow for intuitive use.

The “Connect Lyft” button is now only visible at initial entry and once the Lyft token expires (after 60 minutes).

•••••••••••••••••••

Conclusion

What Worked? I enjoyed the challenge of providing added value by creating an API mashup (Lyft, Google, and Yelp).

What were your doubts going into the project? Successfully implementing a decoupled API and client.

What surprised you the most? How much must be done to secure communication between API’s and clients.

What would you have done differently? Further due diligence before starting to build. On each project I develop, I learn new requirements I need to research before I start writing code.

What did you learn while doing this project? I learned how to create a decoupled API and client app, including security practices that go along with decoupling. I also learned a lot about working with API’s - how to build them and how to implement them with a client.

How will you use that information in the future? I’m confident I will use this information for future projects as many apps are becoming more decoupled. Additionally, most apps either use or provide API’s.

More

See how I worked with React + Redux to create a Mac Desktop App 👉