@dofek/trainerroad
v0.1.4
Published
Unofficial TrainerRoad API client using reverse-engineered cookie-based authentication
Downloads
333
Maintainers
Readme
@dofek/trainerroad
Unofficial TypeScript client for private TrainerRoad web endpoints. It signs in through the observed HTML form flow and retrieves member information, activities, and career data.
This package is not affiliated with, endorsed by, or supported by TrainerRoad. Its authentication page and undocumented endpoints may change without notice.
Requirements and installation
Requires Node.js 22.14 or newer and its built-in
fetch implementation.
npm install @dofek/trainerroadQuick start
Save this as example.mjs, set TRAINERROAD_USERNAME and
TRAINERROAD_PASSWORD, then run node example.mjs.
import { TrainerRoadClient } from "@dofek/trainerroad";
const login = process.env.TRAINERROAD_USERNAME;
const password = process.env.TRAINERROAD_PASSWORD;
if (!login || !password) {
throw new Error("Set TRAINERROAD_USERNAME and TRAINERROAD_PASSWORD");
}
const { authCookie, username } = await TrainerRoadClient.signIn(
login,
password,
);
const client = new TrainerRoadClient(authCookie);
const activities = await client.getActivities(
username,
"2026-07-01",
"2026-07-07",
);
console.log(activities);Dates passed to getActivities use YYYY-MM-DD.
Public API
TrainerRoadClient.signIn(username, password, fetch?)returns the observedSharedTrainerRoadAuthcookie value and canonical account username.new TrainerRoadClient(authCookie, fetch?)creates a session-backed client.client.getMemberInfo()retrieves the current member's ID and username.client.getActivities(username, startDate, endDate)retrieves scheduled and completed calendar activities.client.getCareer(username)retrieves career data such as FTP and weight.
Supported deep imports:
@dofek/trainerroad/client— client class.@dofek/trainerroad/parsing— activity type mapping andparseTrainerRoadActivity.@dofek/trainerroad/types— raw member, activity, and career interfaces.
import { parseTrainerRoadActivity } from "@dofek/trainerroad/parsing";
import type { TrainerRoadActivity } from "@dofek/trainerroad/types";Authentication and persistence
The current implementation
loads /app/login, extracts the __RequestVerificationToken CSRF field and
initial cookies, submits the login form, and returns the
SharedTrainerRoadAuth cookie set by the response. It does not use embedded
application credentials.
Treat authCookie as a password-equivalent session secret and keep it in
encrypted storage. The package cannot inspect its expiry and has no cookie
refresh endpoint. If an authenticated request begins failing, call signIn
again and replace the stored cookie. Never log or commit the cookie or account
password.
Rate limits and errors
The shared
rate-limit wrapper
throws ProviderRateLimitError for 429 and
ProviderServiceUnavailableError for 502, 503, and 504. Both expose
providerId, statusCode, responseBody, and retryAfterSeconds. The latter
follows the HTTP
Retry-After
header when present. Other unsuccessful API responses throw a regular Error;
a failed login without the expected session cookie also throws Error.
If your application handles these error classes directly, declare
@dofek/provider-http as a direct dependency:
npm install @dofek/provider-httpimport {
ProviderRateLimitError,
ProviderServiceUnavailableError,
} from "@dofek/provider-http/rate-limit";
try {
await client.getCareer(username);
} catch (error) {
if (
error instanceof ProviderRateLimitError ||
error instanceof ProviderServiceUnavailableError
) {
console.error(error.providerId, error.statusCode, error.retryAfterSeconds);
}
throw error;
}Parsing behavior
parseTrainerRoadActivity maps the currently observed activity names and
IsOutside flag to canonical activity types. It treats CompletedDate as the
end time and derives the start by subtracting Duration.
