Skip to content

paladini/node-intervals-icu

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

28 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

intervals-icu

A lightweight TypeScript client library for the Intervals.icu API. Supports all major endpoints including athletes, events, wellness, workouts, and activities.

Features

  • ๐Ÿš€ Lightweight - Zero dependencies beyond axios
  • ๐Ÿ“˜ Fully Typed - Written in TypeScript with comprehensive type definitions
  • ๐ŸŒณ Tree-shakeable - ESM exports for optimal bundle size
  • ๐Ÿ” Authentication - Built-in support for API key authentication
  • โšก Rate Limiting Awareness - Tracks and exposes rate limit information
  • ๐Ÿ›ก๏ธ Error Handling - Robust error handling with custom error types
  • ๐Ÿ“– Well Documented - JSDoc comments on all public methods

Installation

npm install intervals-icu

Quick Start

import { IntervalsClient } from 'intervals-icu';

// Initialize the client
const client = new IntervalsClient({
  apiKey: 'your-api-key-here',
  athleteId: 'i12345' // optional, defaults to 'me'
});

// Get athlete information
const athlete = await client.getAthlete();
console.log(`Athlete: ${athlete.name}, FTP: ${athlete.ftp}`);

// Get events
const events = await client.getEvents({
  oldest: '2024-01-01',
  newest: '2024-12-31'
});

// Create a wellness entry
await client.createWellness({
  date: '2024-01-15',
  weight: 70,
  restingHR: 50,
  sleepSecs: 28800
});

API Reference

Client Configuration

interface IntervalsConfig {
  apiKey: string;        // Required: Your Intervals.icu API key
  athleteId?: string;    // Optional: Athlete ID (defaults to 'me')
  baseURL?: string;      // Optional: API base URL
  timeout?: number;      // Optional: Request timeout in ms (default: 10000)
}

Athlete Methods

getAthlete(athleteId?: string): Promise<Athlete>

Get athlete information.

const athlete = await client.getAthlete();
console.log(athlete.name, athlete.ftp, athlete.weight);

updateAthlete(data: Partial<Athlete>, athleteId?: string): Promise<Athlete>

Update athlete information.

const updated = await client.updateAthlete({
  ftp: 250,
  weight: 70
});

Event Methods

getEvents(options?: PaginationOptions, athleteId?: string): Promise<Event[]>

Get calendar events.

const events = await client.getEvents({
  oldest: '2024-01-01',
  newest: '2024-12-31'
});

getEvent(eventId: number, athleteId?: string): Promise<Event>

Get a specific event by ID.

const event = await client.getEvent(12345);

createEvent(data: EventInput, athleteId?: string): Promise<Event>

Create a new calendar event.

const event = await client.createEvent({
  start_date_local: '2024-01-15',
  name: 'Race Day',
  category: 'RACE',
  description: 'Important race'
});

updateEvent(eventId: number, data: Partial<EventInput>, athleteId?: string): Promise<Event>

Update an existing event.

await client.updateEvent(12345, {
  name: 'Updated Race Day'
});

deleteEvent(eventId: number, athleteId?: string): Promise<void>

Delete an event.

await client.deleteEvent(12345);

Filtering Events by Type

Events now include a type field that allows you to reliably differentiate between different event types (Run, Ride, Swim, Strength, etc.) without relying on name patterns:

// Get all events
const allEvents = await client.getEvents({
  oldest: '2024-01-01',
  newest: '2024-12-31'
});

// Filter only running events
const runEvents = allEvents.filter(event => event.type === 'Run');

// Filter only cycling events
const rideEvents = allEvents.filter(event => event.type === 'Ride');

// Filter only swimming events
const swimEvents = allEvents.filter(event => event.type === 'Swim');

// Filter only strength training events
const strengthEvents = allEvents.filter(event => event.type === 'Strength');

// Find a specific running workout
const runWorkout = allEvents.find(e => 
  e.category === 'WORKOUT' && e.type === 'Run'
);

This approach is:

  • โœ… Reliable and type-safe
  • โœ… Language-agnostic (works with any language/locale)
  • โœ… More maintainable than name-based pattern matching

Wellness Methods

getWellness(options?: PaginationOptions, athleteId?: string): Promise<Wellness[]>

Get wellness data.

const wellness = await client.getWellness({
  oldest: '2024-01-01',
  newest: '2024-01-31'
});

createWellness(data: WellnessInput, athleteId?: string): Promise<Wellness>

Create a new wellness entry.

const wellness = await client.createWellness({
  date: '2024-01-15',
  weight: 70,
  restingHR: 50,
  hrv: 65,
  sleepSecs: 28800,
  sleepQuality: 8
});

updateWellness(date: string, data: Partial<WellnessInput>, athleteId?: string): Promise<Wellness>

Update a wellness entry for a specific date.

await client.updateWellness('2024-01-15', {
  weight: 69.5,
  mood: 8
});

deleteWellness(date: string, athleteId?: string): Promise<void>

Delete a wellness entry.

await client.deleteWellness('2024-01-15');

Workout Methods

getWorkouts(options?: PaginationOptions, athleteId?: string): Promise<Workout[]>

Get planned workouts.

const workouts = await client.getWorkouts({
  oldest: '2024-01-01',
  newest: '2024-01-31'
});

getWorkout(workoutId: number, athleteId?: string): Promise<Workout>

Get a specific workout by ID.

const workout = await client.getWorkout(12345);

createWorkout(data: WorkoutInput, athleteId?: string): Promise<Workout>

Create a new workout.

const workout = await client.createWorkout({
  start_date_local: '2024-01-15',
  name: 'Tempo Run',
  description: '45 min tempo at threshold',
  duration_secs: 2700,
  tss: 65
});

updateWorkout(workoutId: number, data: Partial<WorkoutInput>, athleteId?: string): Promise<Workout>

Update an existing workout.

await client.updateWorkout(12345, {
  name: 'Updated Tempo Run',
  tss: 70
});

deleteWorkout(workoutId: number, athleteId?: string): Promise<void>

Delete a workout.

await client.deleteWorkout(12345);

Activity Methods

getActivities(options?: PaginationOptions, athleteId?: string): Promise<Activity[]>

Get recorded activities.

const activities = await client.getActivities({
  oldest: '2024-01-01',
  newest: '2024-01-31'
});

getActivity(activityId: number, athleteId?: string): Promise<Activity>

Get a specific activity by ID.

const activity = await client.getActivity(12345);

updateActivity(activityId: number, data: ActivityInput, athleteId?: string): Promise<Activity>

Update an existing activity.

await client.updateActivity(12345, {
  name: 'Morning Run',
  description: 'Easy recovery run',
  feel: 8
});

deleteActivity(activityId: number, athleteId?: string): Promise<void>

Delete an activity.

await client.deleteActivity(12345);

Rate Limiting

The client automatically tracks rate limit information from the API responses:

// Make some API calls
await client.getAthlete();

// Check rate limit status
const remaining = client.getRateLimitRemaining();
const resetTime = client.getRateLimitReset();

console.log(`Remaining: ${remaining}, Resets at: ${resetTime}`);

Error Handling

The client throws IntervalsAPIError for all API-related errors:

import { IntervalsClient, IntervalsAPIError } from 'intervals-icu';

try {
  const athlete = await client.getAthlete();
} catch (error) {
  if (error instanceof IntervalsAPIError) {
    console.error(`API Error: ${error.message}`);
    console.error(`Status: ${error.status}`);
    console.error(`Code: ${error.code}`);
    
    // Handle specific error types
    if (error.code === 'RATE_LIMIT_EXCEEDED') {
      console.error('Rate limit exceeded, try again later');
    } else if (error.code === 'AUTH_FAILED') {
      console.error('Invalid API key');
    }
  }
}

TypeScript Support

All methods and responses are fully typed. Import types as needed:

import type {
  Athlete,
  Event,
  Wellness,
  Workout,
  Activity,
  PaginationOptions,
  IntervalsConfig
} from 'node-intervals-icu';

Authentication

To get your API key:

  1. Log in to Intervals.icu
  2. Go to Settings โ†’ API Key
  3. Copy your API key

Your athlete ID can be found in the URL when viewing your profile (e.g., i12345), or you can use 'me' to refer to the authenticated athlete.

License

MIT License - Copyright (c) 2025 Fernando Paladini

See the LICENSE file for details.

Acknowledgments

Special thanks to Filipe for the inspiration and the initial idea that led to the creation of this library. Your project was the spark that made this happen! ๐Ÿ™

Contributing

Contributions are welcome! Please read our Contributing Guide to learn how you can help make this project better.

Publishing

For maintainers: See the Publishing Guide for detailed instructions on publishing this library to NPM.

Links

About

A lightweight TypeScript / Javascript client library for the Intervals.icu API.

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Packages

No packages published

Contributors 2

  •  
  •