GarminClient
The GarminClient class provides programmatic access to the Garmin Connect API with automatic token management.
Constructor
import { GarminClient } from '@wellpipe/garmin';
const client = new GarminClient(tokenProvider: GarminTokenProvider, options?: GarminClientOptions);
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
tokenProvider | GarminTokenProvider | Yes | Token management implementation |
options | GarminClientOptions | No | Client configuration |
Options
interface GarminClientOptions {
/** Base URL for Garmin API (default: https://connectapi.garmin.com) */
baseUrl?: string;
}
Token Provider
The GarminTokenProvider manages OAuth tokens and automatic refresh:
import { GarminTokenProvider, FileTokenStorage } from '@wellpipe/garmin';
// File-based storage
const storage = new FileTokenStorage('~/.garmin');
const tokenProvider = new GarminTokenProvider(storage);
// Or memory-based for testing
import { MemoryTokenStorage } from '@wellpipe/garmin';
const memoryStorage = new MemoryTokenStorage();
Setting Tokens
After login, store the tokens:
await tokenProvider.setTokens(oauth1Token, oauth2Token);
Token Storage Interface
Implement custom storage:
interface GarminTokenStorage {
getOAuth1Token(): Promise<OAuth1Token | null>;
getOAuth2Token(): Promise<OAuth2Token | null>;
setOAuth1Token(token: OAuth1Token): Promise<void>;
setOAuth2Token(token: OAuth2Token): Promise<void>;
clearTokens(): Promise<void>;
}
API Methods
Low-Level HTTP
The client provides generic HTTP methods:
// GET request
const data = await client.get<ResponseType>('/endpoint', { param: 'value' });
// POST request
const result = await client.post<ResponseType>('/endpoint', { body: 'data' });
// PUT request
const updated = await client.put<ResponseType>('/endpoint', { body: 'data' });
// DELETE request
await client.delete<ResponseType>('/endpoint');
// Download binary data (FIT files, etc.)
const buffer = await client.download('/endpoint');
Sleep APIs
import { getSleepByDate, getSleepRange, getSleepList } from '@wellpipe/garmin';
// Get sleep for a specific date
const sleep = await getSleepByDate(client, '2024-01-15');
// Get sleep data range
const sleepRange = await getSleepRange(client, '2024-01-01', '2024-01-07');
// Get recent sleep list
const sleepList = await getSleepList(client, 7); // Last 7 days
Heart Rate APIs
import { getHeartRateByDate, getRestingHeartRate } from '@wellpipe/garmin';
// All-day heart rate
const heartRate = await getHeartRateByDate(client, '2024-01-15');
// Resting heart rate
const restingHR = await getRestingHeartRate(client, '2024-01-15');
HRV APIs
import { getHRVByDate, getHRVStatus } from '@wellpipe/garmin';
// Daily HRV
const hrv = await getHRVByDate(client, '2024-01-15');
// HRV status with baseline comparison
const status = await getHRVStatus(client);
Stress APIs
import { getStressByDate, getBodyBattery } from '@wellpipe/garmin';
// Daily stress levels
const stress = await getStressByDate(client, '2024-01-15');
// Body Battery
const battery = await getBodyBattery(client, '2024-01-15');
Activity APIs
import {
getActivities,
getActivityById,
getActivityDetails,
downloadActivityFit,
} from '@wellpipe/garmin';
// List recent activities
const activities = await getActivities(client, 0, 20);
// Get activity by ID
const activity = await getActivityById(client, 'activity-id');
// Get detailed activity data
const details = await getActivityDetails(client, 'activity-id');
// Download FIT file
const fitData = await downloadActivityFit(client, 'activity-id');
Body APIs
import { getWeight, getHydration } from '@wellpipe/garmin';
// Weight history
const weights = await getWeight(client, '2024-01-01', '2024-01-31');
// Hydration data
const hydration = await getHydration(client, '2024-01-15');
Training APIs
import {
getTrainingStatus,
getTrainingReadiness,
getVO2Max,
getRacePredictions,
getPersonalRecords,
} from '@wellpipe/garmin';
// Training status
const status = await getTrainingStatus(client);
// Training readiness
const readiness = await getTrainingReadiness(client);
// VO2 Max
const vo2max = await getVO2Max(client);
// Race predictions
const predictions = await getRacePredictions(client);
// Personal records
const records = await getPersonalRecords(client);
Error Handling
The client throws typed errors for different failure scenarios:
import { GarminApiError } from '@wellpipe/garmin';
try {
const data = await client.get('/endpoint');
} catch (error) {
if (error instanceof GarminApiError) {
console.error('API error:', error.status, error.statusText);
if (error.isAuthError()) {
// Token expired or invalid (401, 403)
console.error('Re-authentication required');
}
if (error.isRateLimited()) {
// Rate limit exceeded (429)
console.error('Rate limited, try again later');
}
} else {
throw error;
}
}
GarminApiError
class GarminApiError extends Error {
status: number; // HTTP status code
statusText: string; // HTTP status text
body?: unknown; // Response body if available
isAuthError(): boolean; // Returns true for 401, 403
isRateLimited(): boolean; // Returns true for 429
}
Authentication Flow
Initial Login
import { login, completeMFALogin } from '@wellpipe/garmin';
async function authenticate(email: string, password: string) {
const result = await login(email, password);
if ('needs_mfa' in result) {
// MFA required - get code from user
const mfaCode = await promptUserForMFA();
const tokens = await completeMFALogin(mfaCode, result);
return tokens;
}
// Login successful
return result;
}
Token Refresh
OAuth2 tokens expire but can be refreshed using OAuth1 credentials:
import { refreshOAuth2Token, isOAuth2Expired } from '@wellpipe/garmin';
// The GarminTokenProvider handles this automatically
// But you can do it manually if needed:
if (isOAuth2Expired(oauth2Token)) {
const newOAuth2 = await refreshOAuth2Token(oauth1Token);
}
Domain Handling
Garmin uses different API domains per region. The token provider automatically detects the correct domain from the OAuth1 token:
// Domain is determined from OAuth1 token consumer key
const domain = tokenProvider.getDomain();
// Returns: 'garmin.com' or 'garmin.cn'
// Client uses: https://connectapi.{domain}
Usage Example
Complete example showing authentication and data fetching:
import {
GarminClient,
GarminTokenProvider,
FileTokenStorage,
login,
completeMFALogin,
} from '@wellpipe/garmin';
import * as readline from 'readline';
async function main() {
const storage = new FileTokenStorage('~/.garmin');
const tokenProvider = new GarminTokenProvider(storage);
// Check if we have existing tokens
if (await tokenProvider.hasValidTokens()) {
console.log('Using existing tokens');
} else {
// Need to authenticate
const result = await login(email, password);
if ('needs_mfa' in result) {
const code = await promptForMFA();
const tokens = await completeMFALogin(code, result);
await tokenProvider.setTokens(tokens.oauth1, tokens.oauth2);
} else {
await tokenProvider.setTokens(result.oauth1, result.oauth2);
}
}
// Create client and fetch data
const client = new GarminClient(tokenProvider);
const today = new Date().toISOString().split('T')[0];
const sleep = await getSleepByDate(client, today);
console.log('Sleep:', sleep);
}