Skip to content

rfoel/strava

Repository files navigation

Strava

npm npm bundle size NPM

Fully typed JavaScript client for the Strava API. Types are generated from Strava's official OpenAPI spec with @hey-api/openapi-ts; the resource layer is hand-written for a flat, ergonomic call shape.

Install

npm install strava

Requires Node 18+ (uses native fetch / FormData).

Quick start

import { Strava } from 'strava'

const strava = new Strava({
  client_id: '123',
  client_secret: 'abc',
  refresh_token: 'def',
})

const activities = await strava.activities.getLoggedInAthleteActivities()

const activity = await strava.activities.getActivityById(12345, {
  include_all_efforts: true,
})

const stats = await strava.athletes.getStats(987)

Path IDs are positional. Everything else (query, body) lives in a single options object — Stripe-style.

The client refreshes the access token automatically when it's missing or near expiry. Provide an access_token to skip the initial refresh:

const strava = new Strava({
  client_id: '123',
  client_secret: 'abc',
  access_token: {
    access_token: '...',
    refresh_token: '...',
    expires_at: 1735689600,
  },
  on_token_refresh: (token) => {
    db.set('strava_token', token)
  },
})

Authorization code exchange

import { Strava } from 'strava'

const { strava, token } = await Strava.fromAuthorizationCode(
  { client_id: '123', client_secret: 'abc' },
  code,
)

if (token.athlete) console.log('first login by', token.athlete.id)

For building the authorize URL:

import { OAuth } from 'strava'

const url = new OAuth().authorizeUrl({
  client_id: '123',
  redirect_uri: 'https://app.example.com/oauth/callback',
  scope: ['read', 'activity:read_all'],
})

Rate limits

strava.getRateLimit()
// { shortTermLimit, shortTermUsage, longTermLimit, longTermUsage, timestamp }

Or via callback:

new Strava({
  client_id,
  client_secret,
  refresh_token,
  on_rate_limit_update: (rl) => console.log(rl),
})

Errors

Non-2xx responses throw StravaApiError with status, statusText, and data (parsed Fault when JSON).

import { StravaApiError } from 'strava'

try {
  await strava.activities.getActivityById(1)
} catch (e) {
  if (e instanceof StravaApiError) console.log(e.status, e.data)
}

Uploads

import { readFileSync } from 'node:fs'

const file = new Blob([readFileSync('ride.fit')])
const upload = await strava.uploads.createUpload({
  file,
  data_type: 'fit',
  name: 'Morning ride',
})

Push subscriptions (webhooks)

Not in the OpenAPI spec; hand-typed module:

const sub = await strava.subscriptions.create({
  callback_url: 'https://app.example.com/strava/webhook',
  verify_token: 'STRAVA',
})

await strava.subscriptions.list()
await strava.subscriptions.delete(sub.id)

Raw HTTP

For ops not wrapped, drop down to the typed HTTP layer:

const data = await strava.http.request<MyResponse>('GET', '/some/path', {
  query: { foo: 'bar' },
})

Development

npm install
npm test           # vitest run
npm run test:watch
npm run lint
npm run build

Regenerating types

npm run generate

Downloads swagger.json, converts to OpenAPI 3, patches known spec bugs, and regenerates src/generated/types.gen.ts. The output is committed — contributors don't need to run this unless the spec changes.

License

MIT

About

JavaScript wrapper for the Strava JSON API

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Used by

Contributors

Languages