A simple Node.js wrapper for Strava's v3 API
Supports many but not all Strava API endpoints:
oauthathleteathletesactivitiesclubsgearroutessegmentssegment_effortsstreamsuploads
npm install strava-v3Importing only the library:
import strava from 'strava-v3';Importing both the library as well as interfaces:
import { default as strava, Strava } from 'strava-v3';- Create an application at strava.com/settings/api and make note of your
access_token
const strava = require('strava-v3')
strava.config({...})
const payload = await strava.athlete.get({})
console.log(payload)const strava = require('strava-v3');
strava.athlete.get({},function(err,payload,limits) {
if(!err) {
console.log(payload);
}
else {
console.log(err);
}
});If you are writing an app that other Strava users will authorize against their
own account, you'll need to use the OAuth flow. This requires that you provide
a client_id, client_secret and redirect_uri that ultimately result in
getting back an access_token which can be used for calls on behalf of that
user.
You have three options to configure your OAuth calls:
Use explicit configuration, which will override both the config file and the environment variables:
var strava = require('strava-v3')
strava.config({
"access_token" : "Your apps access token (Required for Quickstart)",
"client_id" : "Your apps Client ID (Required for oauth)",
"client_secret" : "Your apps Client Secret (Required for oauth)",
"redirect_uri" : "Your apps Authorization Redirection URI (Required for oauth)",
});You may alternatively supply the values via environment variables named following the convention STRAVA_<keyName>, so
STRAVA_ACCESS_TOKEN=access_tokenSTRAVA_CLIENT_ID=client_idSTRAVA_CLIENT_SECRET=client_secretSTRAVA_REDIRECT_URI=redirect_uri
The template strava_config file can be found at the modules root directory and has the following structure
{
"access_token" : "Your apps access token (Required for Quickstart)",
"client_id" : "Your apps Client ID (Required for oauth)",
"client_secret" : "Your apps Client Secret (Required for oauth)",
"redirect_uri" : "Your apps Authorization Redirection URI (Required for oauth)",
}API access is designed to be as closely similar in layout as possible to Strava's own architecture, with the general call definition being
var strava = require('strava-v3')
// Promise API
strava.<api endpoint>.<api endpoint option>(args)
// Callback API
strava.<api endpoint>.<api endpoint option>(args,callback)Example usage:
var strava = require('strava-v3');
strava.athlete.get({id:12345},function(err,payload,limits) {
//do something with your payload, track rate limits
});You'll may want to use OAuth access_tokens on behalf of specific users once
your app is in production. Using an access_token specific to a validated user
allows for detailed athlete information, as well as the option for additional
PUT/POST/DELETE privileges.
Use app-specific logic to retrieve the access\_token for a particular user, then create a Strava client for that user, with their token:
const stravaApi = require('strava-v3');
// ... get access_token from somewhere
strava = new stravaApi.client(access_token);
const payload = await strava.athlete.get({})Less conveniently, you can also explicitly pass an access_token to API calls:
Example usage:
const strava = require('strava-v3');
const payload = await strava.athlete.get({'access_token':'abcde'})For those API calls that support pagination, you can control both the page being retrieved and the number of responses to return per_page by adding the corresponding properties to args.
Example usage:
const strava = require('strava-v3');
const payload = await strava.athlete.listActivities({
page: 1,
per_page: 2
});To upload a file you'll have to pass in the data_type as specified in Strava's API reference as well as a string file designating the <filepath>/<filename>. If you want to get updates on the status of your upload pass in statusCallback along with the rest of your args - the wrapper will check on the upload once a second until complete.
Example usage:
const strava = require('strava-v3');
const payload = await strava.uploads.post({
data_type: 'gpx',
file: 'data/your_file.gpx',
name: 'Epic times',
statusCallback: (err,payload) => {
//do something with your payload
}
});According to Strava's API each response contains information about rate limits. For more details see: Rate Limits
Returns null if X-Ratelimit-Limit, X-RateLimit-Usage, X-ReadRateLimit-Limit, or
X-ReadRateLimit-Usage headers are not provided
In our promise API, only the response body "payload" value is returned as a Promise. To track
rate limiting we use a global counter accessible through strava.rateLimiting.
The rate limiting status is updated with each request.
// returns true if the most recent request exceeded the overall rate limit
strava.rateLimiting.exceeded()
// returns the current decimal fraction (from 0 to 1) of overall rate used. The greater of the short and long term limits.
strava.rateLimiting.fractionReached()
// returns true if the most recent request exceeded the read rate limit
strava.rateLimiting.readExceeded()
// returns the current decimal fraction (from 0 to 1) of read rate used. The greater of the short and long term limits.
strava.rateLimiting.readFractionReached()const strava = require('strava-v3');
strava.athlete.get({'access_token':'abcde'},function(err,payload,limits) {
//do something with your payload, track rate limits
console.log(limits);
/*
output:
{
shortTermUsage: 3,
shortTermLimit: 200,
longTermUsage: 12,
longTermLimit: 2000,
readShortTermUsage: 2,
readShortTermLimit: 100,
readLongTermUsage: 5,
readLongTermLimit: 1000
}
*/
});To used the Promise-based API, do not provide a callback. A promise will be returned.
See Strava API docs for returned data structures.
strava.oauth.getRequestAccessURL(args)strava.oauth.getToken(code,done)(Used to token exchange)strava.oauth.refreshToken(code)(Callback API not supported)strava.oauth.deauthorize(args,done)
strava.athlete.get(args,done)strava.athlete.update(args,done)// only 'weight' can be updated.strava.athlete.listActivities(args,done)Get list of activity summariesstrava.athlete.listClubs(args,done)strava.athlete.listZones(args,done)
strava.athletes.stats(args,done)
strava.activities.get(args,done)strava.activities.create(args,done)strava.activities.update(args,done)strava.activities.listZones(args,done)strava.activities.listLaps(args,done)strava.activities.listComments(args,done)strava.activities.listKudos(args,done)
strava.clubs.get(args,done)strava.clubs.listMembers(args,done)strava.clubs.listActivities(args,done)strava.clubs.listAdmins(args,done)
strava.gear.get(args,done)
These methods Authenticate with a Client ID and Client Secret. Since they don't
use OAuth, they are not available on the client object.
strava.pushSubscriptions.list({},done)strava.pushSubscriptions.create({callback_url:...},done)- We set 'object_type to "activity" and "aspect_type" to "create" for you.
strava.pushSubscriptions.delete({id:...},done)
strava.routes.getFile({ id: routeId, file_type: 'gpx' },done)file_type may also be 'tcx'strava.routes.get(args,done)
strava.segments.get(args,done)strava.segments.listStarred(args,done)strava.segments.listEfforts(args,done)strava.segments.explore(args,done)Expects argboundsas a comma separated string, for two points describing a rectangular boundary for the search:"southwest corner latitude, southwest corner longitude, northeast corner latitude, northeast corner longitude".strava.segments.starSegment(args,done)
strava.segmentEfforts.get(args,done)
strava.streams.activity(args,done)strava.streams.effort(args,done)strava.streams.segment(args,done)strava.streams.route(args,done)
strava.uploads.post(args,done)
Except for the OAuth calls, errors returned will be instances of StatusCodeError when the HTTP status code is not 2xx. In the Promise-based API, the promise will be rejected. An error of type RequestError will be returned if the request fails for technical reasons.
The updated version now uses Axios for HTTP requests and custom error classes for compatibility with the previous implementation.
In the Promise-based API, errors will reject the Promise. In the callback-based API (where supported), errors will pass to the err argument in the callback.
The project no longer relies on Bluebird. Where applicable, callback handling has been removed.
Example error checking:
const { StatusCodeError, RequestError } = require('./axiosUtility');
// Catch a non-2xx response with the Promise API
badClient.athlete.get({})
.catch((e) => {
if (e.name === 'StatusCodeError') {
// handle StatusCodeError
}
});
// Or handle all errors
badClient.athlete.get({})
.catch((err) => {
console.error(err);
});The StatusCodeError object includes extra properties to help with debugging:
nameis alwaysStatusCodeErrorstatusCodecontains the HTTP status codemessagecontains the response's status message and additional error detailsdatacontains the body of the response, which can be useful for debuggingoptionscontains the options used in the requestresponsecontains the response object
The RequestError object is used for errors that occur due to technical issues, such as no response being received or request setup issues, and includes the following properties:
nameis alwaysRequestErrormessagecontains the error messageoptionscontains the options used in the request
This update maintains feature parity with the previous implementation of request-promise while using the Axios HTTP client under the hood.
This package includes a full test suite runnable via yarn test.
It will both lint and run tests on API endpoints.
All tests use nock to mock the Strava API and can run without any real credentials or network access.
Simply run:
yarn testThe test suite will:
- Run ESLint on all JavaScript files
- Execute all unit tests using mocked API responses
- Tests use Mocha and Node.js's built-in
assertmodule - HTTP interaction is performed with Axios; all tests mock HTTP requests using
nock
The test suite validates:
- All
GETendpoints return the correct data structure - All
POST/PUT/DELETEendpoints handle requests and responses correctly - Error handling works as expected
- Rate limiting functionality is properly tested
Authored by Austin Brown austin@unboundev.com (http://austinjamesbrown.com/).
Currently Maintained by Mark Stosberg mark@rideamigos.com and Wesley Schlenker wesley@rideamigos.com