Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Strava Laravel Package

codetoad/strava

Laravel package for integrating the Strava API into your app. It simplifies OAuth authentication and access to athlete, activity, and stats data, making it easier to build fitness, training, and activity-tracking features with Strava.

View on GitHub
Deep Wiki
Context7

Getting Started

  1. composer require codetoad/strava
  2. Publish config + migrations: php artisan vendor:publish --provider="CodeToad\Strava\StravaServiceProvider" then php artisan migrate. The migration creates the table the package uses to persist OAuth tokens (usually one row per user).
  3. Register a Strava API application at https://www.strava.com/settings/api. Note the client ID/secret; the Authorization Callback Domain field on Strava takes a domain only, while the full redirect_uri goes in your .env.
  4. Set STRAVA_CLIENT_ID, STRAVA_CLIENT_SECRET, STRAVA_REDIRECT_URI (e.g. https://app.test/strava/callback).
  5. Wire two routes: one that hands the user off to Strava's consent screen, one that receives the callback and exchanges the code for tokens via the facade. From there, call the athlete endpoint to confirm the connection works and render the athlete's name on a settings page.

Look first at config/strava.php, the auto-discovered facade alias, and the CodeToad\Strava\Strava class itself — it's small, so read the method list rather than guessing signatures.

Implementation Patterns

OAuth flow (controller-based):

Route::get('/strava/connect', fn (Strava $strava) => $strava->redirect());
Route::get('/strava/callback', function (Strava $strava) {
    $strava->token(request('code'));  // exchanges + persists tokens
    return redirect()->route('dashboard');
})->middleware('auth');

Resolve the client after auth() so tokens are associated with the correct user (the shipped migration has a user_id foreign key for this). Never treat the client as a process-wide singleton if you support more than one athlete.

Common day-to-day calls:

  • $strava->athlete() — profile, measurement preference, location.
  • $strava->athleteStats($athleteId) — YTD and recent totals for a dashboard header.
  • $strava->activities($page, $perPage, $before, $after) — the workhorse; pass Unix after/before for incremental syncs.
  • $strava->activity($id, $includeAllEfforts) — detail view with segment efforts.
  • $strava->streams($id, ['time','latlng','heartrate','watts']) — chart/heatmap feeds.
  • $strava->upload($file, 'gpx', ...) then poll the upload endpoint until status is ready.

Sync workflow: a scheduled job per user pulls activities with after = max(local created_at), upserts into your own activities table keyed on Strava's numeric ID, then a second job hydrates streams for rows missing them. Chunk per user, respect the 15-minute window, and track a last_synced_at column.

Webhooks over polling: subscribe once at the app level to Strava's push_subscriptions endpoint, answer the one-time GET challenge, and on POST events queue a fetch for object_id. This is dramatically cheaper than a 15-minute loop and is the right default once you have more than a handful of athletes.

Testing: wrap the package in your own StravaClient service so you can swap it for a fake bound in the container. Faking the facade directly is brittle.

Gotchas and Tips

  • Rate limits are per application, not per user. Historically 100 requests / 15 min and 1000 / day. One noisy backfill starves every user — queue everything, back off on 429, and inspect the X-RateLimit-* response headers before you retry.
  • Refresh-token rotation. Every refresh returns a new refresh token; if you don't persist it, the user is silently logged out. Cast the columns encrypted and write them atomically.
  • Access tokens expire in ~6 hours. Long-running queue workers must re-resolve the client per job rather than cache it.
  • redirect_uri must match character-for-character what you configured. The Strava dashboard's "Authorization Callback Domain" is not the same field — put the domain there, the full URI in your request.
  • Scopes matter. Default read won't expose private activities or streams. Request activity:read_all (and read for profile/stats). An empty streams payload is almost always a scope problem.
  • Strava returns raw units: meters, seconds, m/s, watts, meters of elevation. Store raw and convert at render time.
  • Paginate deliberately. Activities cap at 200 per page; a loop without an after bound will re-download the entire history every run. Default before to the newest known activity ID/time.
  • 204 No Content on some mutations (deletes, update). Guard against null before calling ->json().
  • Uploads are asynchronous. A 201 means accepted, not processed. Poll the upload ID and treat error statuses (duplicate of ..., malformed) as first-class cases in your UI.
  • Webhook verification must respond in under 2 seconds with the raw hub.challenge value as JSON; a Laravel route returning the array from request('hub.challenge') is enough.
  • In queued jobs, auth() is empty. Pass the user (or resolved token) explicitly to the job — otherwise the client resolves to the wrong account or throws.
  • Log raw requests/responses at debug. Strava errors are terse ({"message":"Authorization Error","errors":[...]}); without the response body you will spend hours guessing.
  • The package is small and low-traffic (74★, no known dependents). Pin the version in composer.json and verify method signatures against vendor/codetoad/strava/README.md at install time rather than relying on blog posts.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle