Hacking My Gym With Node.js

Banky Adebajo
The Startup
Published in
6 min readOct 10, 2020
Photo by Risen Wang on Unsplash

With pandemic measures in place, gyms everywhere have taken precautions to only let a small number of people work out at a time. My gym in particular did this using a website or mobile app, that allows members to book slots. Each days’ slots were opened up for booking exactly a week before the booking day, so the slots for October 14th are opened at midnight on October 7th for example.

The timing that bookings opened was really annoying for me because I typically go to bed at around 10.30pm and when I’m up at 6.30am, all the slots are booked! I didn’t want to have to wait until midnight every night to do the booking, so I decided to find a way to automate the process.

Investigation

First off, I had to do a bit of digging to figure out if this is even possible. I popped open the developer tools in my browser and checked what happens when I made a booking.

Booking POST Request

After hitting the book button, I saw this POST request that does the booking. Great! Looking closer at the request

Booking POST Request Details

The ClubId is unique for the gym branch that I go to, and I guessed that TimeslotId is a unique identifier for the time slot that I just booked. The ClubId won’t change (since I go to the same club every time), but I’ll need a way to find the TimeslotId every day. I thought the front end must be getting the that info since it knows which one to send back to the server. Sure enough, I found this request in the network tab:

GET /club-occupancy/club-workout-schedule?club=268&day=2020-10-08&studio=Gym%20Floor

Which gives a response that looked like this

Perfect, the TimeslotId looks exactly like the Id field in each of these objects. So given a ClubId, a date and time, I can make a booking.

Prototype

When working with APIs, I like to make sure everything works with a program like Insomnia. It’s kind of like the popular Postman app, I’ve grown to prefer it because of its native GraphQL features and nice interface.

☹️ The request was unauthorized, meaning I probably have to do a login request and pass in some authorization parameters as well. I went back to the web page to grab the login endpoint and used it to grab a cookie. Voila:

And now trying to make a booking:

Amazing, I got an email shortly after saying my booking was successful. Now to just put all this into JavaScript 😌.

Writing code

Diving right in, I need three functions for the three API calls (login, getBookingSlots and makeBooking). Starting with the login function, it needs to accept a username and password and return a cookie that the other two functions can use for authorization.

I use the FormData library to format data for the request since the endpoint expects Form URL Encoded data. I am also using Axios for the requests since it has a nice promise based interface. I looked at the formatted cookies that were used by Insomnia, and mimicked them using a reducer to remove the extra pieces. Next, using this I need a function to grab the booking slots for a specified club, date and time.

I opted to use day.js for handling dates because it makes it really easy to format dates, and do date calculations. Earlier, I showed the response from this endpoint comes back as an object that kind of looks like

{
MorningList: { ... },
AfternoonList: { ... },
...
}

This time of day separation is pretty useless so I reduce it down, with ES6 spread syntax, into just one array of time slots that can then be filtered easily. Okay last helper function, one to actually do the booking.

All the pieces are now in place, I need to actually orchestrate the calls. I needed this code to run once every morning right around midnight on the days 1 week before I wanted to go to the gym. Day.js comes in really useful here for doing some date math. To run once per day, I used the built in setTimeout function. I get the next time to try to make a booking as 30 seconds past midnight the following day.

There’s a bit more stuff going on here. I have an array of the days I actually want to go to the gym, and if the current day is not one of those, I just try again a few seconds after midnight tomorrow.

Next, the functions I created earlier are called in sequence to login, get the bookings and actually create the booking on my behalf. I’d like a booking at 7.30am every time, so I find the time slot with that time specified.

There is a bit of error handling, where if the request fails for whatever reason, the entire process is retried after waiting for 1 minute. If it fails 5 times, it gives up and just tries tomorrow.

Problems

There were a few problems I encountered while working on and using this. Firstly, I wanted to have the script running all the time, so I decided to throw it onto a Heroku instance. However, I soon found that since it was idle for so long, my free dyno was put to sleep 😬. To get around this, I ended up creating an express server in the app and having it ping itself every 5 minutes to keep the dyno alive.

The second problem I faced was a realization that happened after the app tried to make a booking on a public holiday. There were no slots available, and the program ended up crashing when I tried to read the error message off of an undefined response. So now I have no time booked for the day after the holiday 🙃. This one made me realize that it’s probably a good idea to have my console.log messages go to slack instead of just being logged into the void. That’s one improvement I’ll eventually come around to.

Conclusion

The full source is available here. This was a pretty fun little way to make my life a bit easier and didn’t require a lot of work to get done. I’ve found that I enjoy tinkering with little things like this that are actually useful to me. I hope you enjoyed the read.

Cheers

--

--