bene : studio is a global consultancy, helping startups, enterprises and HealthTech companies to have better product
Integrating Garmin into a running app
At bene : studio we love knowledge sharing to help the community of professionals. With 10+ years and over 100 projects behind us, we have a vast amount of experience. This is why we have launched a knowledge base on our blog with regular updates of tutorials, best practices, and open source solutions.
These materials come from our internal workshops with our team of developers and engineers.
Developers often face the situation when they need to use a third-party library for integration, mostly requested by the client of their work project. It was no exception when I had to integrate Garmin into one of my projects; Coopah, a running coach as a mobile application made for runners. As you may know, Garmin is a company that specializes in GPS technology for automotive, marine, outdoor, and sports activities. Our focus was solely on running activities.
Coopah already had features, here are a few of them.
Training plans are created by an admin user. A plan is a list of sessions with different intensity running exercises for a given period. E.g. a 6-week plan for 10km running. Or an 18-week plan for marathon training.
Users during registration (and later anytime) can set their goal of distance and the period during which they want to achieve that distance. After this, sessions are distributed for this period and can be viewed/edited. Each session is a list of interval(s), these are goals for a session, like 10 min running at a 5:20 min/km pace
The goal was to:
- Create Garmin workouts from the user’s training sessions
- Match an existing Garmin running activity to the user’s session
To start working on integrating Garmin, you must be part of their Developer program. That is when you get access and documentation to the API and services.
The business logic
Unlike most integrations, this one is not about adding an SDK to your mobile application, but instead providing callback endpoints from your backend to Garmin but more on that later.
To access their services you must implement OAuth1 for authentication. This means that the user authenticates with his/her Garmin account on the mobile device and at the end of the steps this results in a user access token stored in the database. The whole flow is the following:
- The user pressed Connect to Garmin button, a request was made to the backend
- Backend calls request_token endpoint of Garmin API, gives back an URL containing oauth_token and oauth_token_secret values
- The mobile app receives the oauthConfirm URL containing the oauth_token
- Mobile opens this URL, user logs in with his/her Garmin account and as a result, gets an oauth_verifier value
- Call backend with this value
- Backend requests to Garmin API’s access_token endpoint and get back the access_token and access_token_secret values.
- Stores these last two values in the database
Image representation of the above flow
Once authenticated, the user can create Garmin workouts. And not just create, but delete, update and also schedule them, which makes sense to see your Coopah sessions for one particular day to be on the same day when you open your Garmin app for some running. A workout consists of interval(s). Each interval might have a time, distance, or a pace value associated, but at least one of them.
One little side-note to developers: when you create a workout in the scope of your integration (meaning with the access token acquired as above), the workout can not be edited on Garmin Connect. Garmin Connect is the dashboard tool for tracking, analyzing, and sharing health and fitness activities from your Garmin device, mostly used as a website but the mobile app is also available. It says that workout details are visible in your 3rd party app.
This goes vice-versa: any workout you created in your Garmin app, can not be reached via API using your access token.
Now that we created workouts on Garmin based on the Coopah sessions, users can finally go outdoor and run with their favorite Garmin watch. But how do we get a summary of this activity? Is there a dedicated endpoint to fetch activity data from? Yes, there is. It’s even documented in the developer manual, but you CAN NOT use it to pull data because they disapprove of this method.
Instead, you have to provide endpoints of your backend service and they will (eventually) push recorded activity summaries to you, so they can be processed to your liking.
The difference between pull and push methods is that the pull method is more user-driven. Imagine that a user wants to update his/her activities in the mobile app. This sends a request to our backend which calls the Garmin API to query a list of activities. In the end, the user sees these activities.
On the other hand, the push method is event-driven. When Garmin has a new activity recorded they send it to your backend (to the dedicated endpoint), you can process it and notify the user however you feel like it.
Challenges of the integration
When you join the Garmin Developer Program, you get a developer API key, which is rate limited, but perfectly usable for testing, and prototyping. To acquire a production key, you have to go through the verification process. These are the requirements for that:
- Enabled at least 3 endpoints
- Have data from at least 2 different Garmin users in the last 24h on all these endpoints
- No error, no disallowed method called in the last 24h
The tests can be run by anyone with access to the Developer program. Once all the requirements are passed, you can apply for the production key, this is how the process looks like:
Upon applying for production, somebody from Garmin Developer Support will manually check your integration and either approve it or reject it with hints on how to proceed on your side.
Too smart sports watches
An Activity Details Summary is a raw JSON formatted data sent to your backend endpoint once a user completes a run. Garmin, unfortunately, does not provide the exact intervals, but rather two separate lists of measurements.
Here is a (reduced) sample of it:
One is laps, an array of timestamps when a new lap started either automatically or by the user pressing the lap button on the watch. Keep in mind, that Garmin watches have the auto-lap feature auto-enabled, this starts a new lap after every 1 km/mile.
The other array is “samples”, recorded by your watch. Each record contains the current timestamp, speed, pace, duration, etc.
We transformed these two arrays into a list of intervals: looped over the laps and take the difference between the sample at the current lap and the sample at the previous lap. But, here’s the catch! (Most) Garmin watches have smart recording enabled. This means that it does not record every 1 sec, but when it detects a change in direction/speed. And the worst part is that it is NOT guaranteed that a sample is recorded at the start of a new lap. You want to calculate the distance between two laps, which has 10 minutes difference, but you can only calculate it between the two samples 9:59 minutes apart from each other (see below)
It would have been nice to have it, but we will discuss it in the next chapter, Improvement ideas.
Fit SDK parser
In the previous section, we discussed how inaccurate the measured data was provided by the Activity Details Summary service. We contacted Garmin and they admitted that they sometimes use rounding of recorded data to negate the 1-2 seconds difference.
What they suggested is to implement the FIT SDK (Flexible and Interoperable Data Transfer). They may provide these .FIT files as raw data, and you need to parse them. You can use one of the parsers of their recommendation, but we found that NodeJS is not supported. So we either implement the complex parsing from scratch or use an existing solution.
Whitelist Garmin IP
One security risk we discovered during the integration, is that Garmin requires a public endpoint on our backend, no Authorization is allowed so that they can send data to it. However, any attacker with a valid access token can spam our backend with dummy data since this is exactly how Garmin operates.
To prevent this a possible solution is to whitelist the IP address of Garmin API, so only they can reach this public endpoint.
All in all, this integration was challenging enough to keep us for a few weeks. Learned a lot starting from how to handle a strict verification process, all the way to understanding the importance of having a good Proof of Concept in the early development phase.
Join our team, we are hiring!
Read moreSee all open source
Using Schedulers and Cron in Node.JS
Follow out tutorial and learn about scheduling in Node JS
Principles of wiriting clean code
Lessons we learned from our favourite book on software development.
Why we love Next.js and server-side rendering
You should use Next.js because of its benefits in SEO, Integrated routing, pre-rendering and styling.
Functional programming in Java with examples
See examples if this style of programming, to make code more concise and less complex.
How to become a VSCode power user
In this article, we are going through some of our most commonly used extensions, key bindings, and best practices.
Let bene : studio enhance
your digital product!