Build a Workout Tracker with GraphCMS, Auth0 and Hasura
Warning, things are about to get meshy. Learn how to merge multiple GraphQL schema's together with Hasura's remote schema joins in this reference fitness app. We'll throw in user authenticated, too, just for fun.
TLDR; Here's the important stuff.
If there's a cliche more pronounced than the perennial "starting a fitness resolution" only to abandon it later, it would be developers deciding to build their own fitness program/app instead of using an existing one. That being said, it is a very practical example of how we can solve common problems following well-established best-practices.
Finding your FitAnchor
We've seen a rise in the popularity of micro-communities over the last years and the trend is likely to continue. Even massive social networks like Facebook excel, in part, thanks to the functionality of groups. People like forming micro-tribes in their day-to-day experiences and there will always be room for a new tribe to form. Embracing a lean architecture and best-of-breed service providers will help you build your community quickly and efficiently.
A Tale of Two SchemasAnchor
Our project combines two schemas. I will refer to them as the editorial content and the application content. For our exercise program, we'll store this content as editorial content in GraphCMS. We want/need the flexibility of a robust data modeling solution like GraphCMS to give us the kind of elegant schema that will play well with others. The second piece we need is application data such as user accounts, stored user activity, and exercise history. Application data will be stored in Hasura which will also act as our joining platform to combine the APIs together.
Doing the WorkAnchor
For this demo, we have created a website where you can sign-up, browse workouts, log a workout session, and browse your history.
This is the schema we’ll be building.
In our model we are create small entities composed of even smaller pieces. We are storing base workout movements like a Pushup, then we map that to either a repetition based movement such as "25 pushups" or something like an AMRAP movement (as many repetitions as possible within a minute). The base movements themselves are split between either a bodyweight movement or a movement that requires equipment. Our "repetition based" model, can take a base movement of either type, thanks to our new support for union types. Beyond that, we identify models that are stored in Hasura such as our user and user history.
The service map we will be building looks like this.
The core of our interaction/service model is that a user comes to the application and signs up for an account. This account is created in Auth0 which then creates a mirror of that account in Hasura via a webhook. The user then receives an auth token that includes additional permissions for the Hasura API. These permissions are passed via a bearer auth-token to Hasura in subsequent API requests to either log a new activity or to fetch workout details (in this case, our workout programming is behind an "auth wall" for members only.)
Once a user logs an activity, it creates a new entry in their user data in Hasura. When the user log data gets updated in Hasura, it triggers a webhook that updates GraphCMS where our "workout PIM is" and updates a popularity field so that the workouts themselves could be queried based on popularity metrics without having to first authenticate and aggregate the total user data.
The power here lies in our merging of these two APIs into a unified one. We can use remote joins in Hasura to delegate part of the schema to another API which then lets us iterate our product data in isolation from our application data. This is an incredibly powerful paradigm and Hasura makes it quite trivial to create. In the figure below one can see how the two schemas come together to represent a final, queryable API.
I've again returned to the NextJS platform, as well as Vercel to host both the main application and a webhook service to handle incrementing our popularity field. I've talked before about the benefits of NextJS, and frankly, it's a great tool that I know how to use. But you can work with any stack you know how to use and the outcome will be the same. Our API has been designed to reflect product capability, not an implementation-specific coupling.
Enjoy the project, follow the guide and let us know what you think!