Body composition & weight
Weigh-ins and body-composition records. Note the unit asymmetry: writes send the raw value in the unit you name, reads return GRAMS.
Every method below hangs off a Garmin instance. See Installation & setup for how to construct one:
import { GarminClient, Garmin, FileTokenStore } from "garminconnect-js";
const client = new GarminClient({ tokenStore: new FileTokenStore("./tokens") });
if (!(await client.loadTokens())) throw new Error("not connected to Garmin");
const garmin = new Garmin(client);
8 methods. The verification column says what has been
confirmed against a live Garmin account, not merely unit-tested —
AGENTS.md carries the full evidence per method.
| Method | Verified |
|---|---|
addBodyComposition | live-verified |
addWeighIn | live-verified |
addWeighInWithTimestamps | live-verified |
deleteWeighIn | live-verified |
deleteWeighIns | live-verified |
getBodyComposition | live-verified |
getDailyWeighIns | live-verified |
getWeighIns | live-verified |
addBodyComposition
garmin.addBodyComposition(weight: number, extra?: WeightScaleFields & { timestamp?: string }): Promise<unknown>
const result = await garmin.addBodyComposition(1);
Returns
unknown — Garmin's response is passed through unparsed. Cast it to whatever you need; this library does not model it.
builds a .fit binary in memory (src/util/fit.ts, a hand-rolled FIT encoder) and uploads it via client.upload to /upload-service/upload; weight validated positive/finite, throws GarminError otherwise; passes client.upload's result through unchecked
Verification: live-verified
addWeighIn
garmin.addWeighIn(weightValue: number, unitKey?: "kg" | "lbs", when?: Date): Promise<unknown>
const result = await garmin.addWeighIn(1);
Returns
unknown — Garmin's response is passed through unparsed. Cast it to whatever you need; this library does not model it.
defaults unitKey="kg", when=new Date()
Verification: live-verified
addWeighInWithTimestamps
garmin.addWeighInWithTimestamps(weightValue: number, unitKey?: "kg" | "lbs", dateTimestamp?: string, gmtTimestamp?: string, when?: Date): Promise<unknown>
const result = await garmin.addWeighInWithTimestamps(1);
Returns
unknown — Garmin's response is passed through unparsed. Cast it to whatever you need; this library does not model it.
same raw-value, no-conversion rule as addWeighIn; the two timestamps are COUPLED: the local instant resolves from dateTimestamp if given (naive strings read as LOCAL time) else from when (defaults new Date()), and gmtTimestamp, when omitted, is derived from THAT resolved instant — never independently from when. Both supplied strings are re-formatted rather than forwarded verbatim (a naive gmtTimestamp is read as UTC)
Verification: live-verified
deleteWeighIn
garmin.deleteWeighIn(cdate: string | Date, weightPk: number): Promise<null>
const result = await garmin.deleteWeighIn("2026-09-24", 1);
Returns
null
Verification: live-verified
deleteWeighIns
garmin.deleteWeighIns(cdate: string | Date, deleteAll?: boolean): Promise<number | null>
const result = await garmin.deleteWeighIns("2026-09-24");
Returns
number
no HTTP path of its own: calls getDailyWeighIns, then loops deleteWeighIn per entry; returns null (deletes nothing) if there are zero entries, or more than one entry and deleteAll is not true; otherwise deletes every entry that day and returns the count. IRREVERSIBLE — deletes ALL weigh-ins recorded on cdate when it proceeds
Verification: live-verified
getBodyComposition
garmin.getBodyComposition(startdate: string | Date, enddate?: string | Date): Promise<BodyCompositionRange | null>
const result = await garmin.getBodyComposition("2026-09-24");
Returns
BodyCompositionRange:
| Field | Type | Always present |
|---|---|---|
startDate | string | no |
endDate | string | no |
dailyWeightSummaries | Record<string, unknown>[] | no |
totalAverage | Record<string, unknown> | no |
Plus every other field Garmin sends: this type carries an index signature because the real response is wider than the fields above, which are the ones this library relies on or has observed. Read an actual response before depending on a field that is not listed.
GETs /weight-service/weight/dateRange; enddate defaults to startdate; throws GarminError if startdate > enddate; passes through unchecked. getStatsAndBody (wellness service) now delegates to this instead of inlining its own copy of the same call
Verification: live-verified
getDailyWeighIns
garmin.getDailyWeighIns(cdate: string | Date): Promise<DailyWeighIns | null>
const result = await garmin.getDailyWeighIns("2026-09-24");
Returns
DailyWeighIns:
| Field | Type | Always present |
|---|---|---|
dateWeightList | WeighInEntry[] | no |
totalAverage | Record<string, unknown> | no |
Plus every other field Garmin sends: this type carries an index signature because the real response is wider than the fields above, which are the ones this library relies on or has observed. Read an actual response before depending on a field that is not listed.
GETs /weight-service/weight/dayview/{cdate}?includeAll=true; passes through unchecked
Verification: live-verified
getWeighIns
garmin.getWeighIns(startdate: string | Date, enddate: string | Date): Promise<WeighInRange>
const result = await garmin.getWeighIns("2026-09-24", "2026-09-24");
Returns
WeighInRange:
| Field | Type | Always present |
|---|---|---|
dailyWeightSummaries | Record<string, unknown>[] | no |
totalAverage | Record<string, unknown> | no |
Plus every other field Garmin sends: this type carries an index signature because the real response is wider than the fields above, which are the ones this library relies on or has observed. Read an actual response before depending on a field that is not listed.
Verification: live-verified