Skip to main content

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

ParameterTypeRequiredDescription
tokenProviderGarminTokenProviderYesToken management implementation
optionsGarminClientOptionsNoClient 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);
}