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

Laravel Harvest Sdk Laravel Package

spatie/laravel-harvest-sdk

A Laravel-friendly SDK for the Harvest.com API, with config publishing, credential setup, and container or facade access. It covers users and more, though not the full API—PRs welcome for missing features.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit. The package is an idiomatic Laravel wrapper, not a domain layer: a container-bound Harvest entry point, a facade, a publishable config, and a set of readonly DTOs (UserData, ProjectData, TimeEntryData) hydrated from Harvest v2 JSON. It fits cleanly into a standard Laravel service architecture as a driven adapter at the edge of the application. It does not attempt to own persistence, queueing, or sync state, which is the correct scope for a library at this maturity. The one architectural constraint worth flagging early is that the package exposes transport concerns (pagination objects, raw DTO arrays) directly to callers — so the wrapper we build around it is what determines whether this stays a swappable dependency or becomes load-bearing.

Integration feasibility. High for the covered surface, unknown outside it. The README is explicit and unusually honest: this is not a full Harvest implementation. Documented and demonstrably working are users (me, all), projects (all), and time entries (paginated, date-bounded). Everything else — invoices, estimates, expenses, tasks, roles, clients/contacts, and all write operations including starting/stopping timers — is absent and must be hand-rolled against api.harvestapp.com/v2. Credentials are a static account ID plus a personal access token plus a required User-Agent (Harvest uses it for integration identification; a placeholder here is a production incident waiting to happen). Multi-tenant OAuth, token refresh, and webhook subscription are entirely out of scope for the package.

Technical risk. Ranked:

  1. Coverage risk (high, near-certain). The probability that a roadmap item needs an endpoint the package does not implement is effectively 1.0. This is not a blocker — a Http::withToken() fallback is an afternoon — but it is a recurring scope tax that should be budgeted per epic rather than discovered per sprint.
  2. Adoption/vendor risk (medium). 1 star and 0 dependents means no community backpressure. Spatie is a credible, long-lived maintainer and recent releases (2.1.0 shipping Laravel 13 support) show active upkeep — but a 0-dependent package is deprioritised first when a maintainer's attention narrows. MIT license plus a thin surface area means the worst case is a fork-and-own, not a rewrite.
  3. API drift risk (medium, silent). DTO hydration will not fail loudly if Harvest adds or renames fields; you simply get nulls. This is only mitigated by fixture-based contract tests, which we must own.
  4. Correctness risk on dates (medium, expensive). Harvest's spent_date semantics interact with account and user timezones. For anything touching payroll or invoicing reconciliation, off-by-one-day errors are silent and expensive to unwind.

Key questions for the team before commitment.

  • Which Harvest surfaces does the first two quarters of the roadmap actually require? Enumerate them; then grep the vendored src/ to confirm coverage rather than trusting the README.
  • Read-only sync, or do we need to write back (timers, entries, project updates)? Write support is currently zero and changes the risk profile materially.
  • Single Harvest account (PAT) or per-tenant OAuth? If the latter, the package saves us almost nothing on the auth path.
  • What is the reconciliation story — does any output feed billing or payroll, and who signs off on date/timezone correctness?
  • Is the 1-star, 0-dependent status acceptable to whoever owns long-term maintenance, and is the fork budget approved in principle?

Integration Approach

Stack fit. Requires Laravel with Composer; the package resolves through the service container, uses the framework's HTTP client, and honours standard config-caching behaviour. It will sit comfortably alongside existing Laravel 10/11/12/13 applications. The 2.1.0 Laravel 13 support commit tells us framework-version churn is being tracked, which is the single most important compatibility signal for a Laravel-only dependency.

Migration path. There is nothing to migrate from at install time; the migration concern is the exit ramp. Install behind a first-party interface from day one:

HarvestGateway (our interface)
├── SdkHarvestGateway      → facade/container, covered endpoints
└── HttpHarvestGateway     → raw HTTP fallback for uncovered endpoints

Controllers, jobs, and console commands depend on HarvestGateway, never on Spatie\Harvest\Facades\Harvest. This costs roughly half a day and buys three things: a documented seam for the uncovered 70% of the Harvest API, trivial mocking in tests, and the ability to replace or fork the package without touching call sites. It is the highest-leverage decision in this integration and should be non-negotiable in code review.

Compatibility. Pin the major version in composer.json and let Dependabot handle minors; the changelog is well-formed and releases are tagged, so upgrade review is cheap. Verify that the published config does not conflict with cached-config deployment steps, and confirm whether a base URL override exists — if it does not, integration testing must rely on Http::fake() rather than a local stub server, which is acceptable but constrains test design.

Sequencing.

  1. Week 1 — Prove the pipe. Install, publish config, set real credentials and a genuine User-Agent, resolve Harvest::users()->me()->dto(). A failure here is almost always a token/account mismatch, a junk user agent, or stale config cache.
  2. Week 2 — Read-only sync slice. Projects + time entries into local tables, using an overlapping lookback window (a few days) rather than "since last sync" — that pattern absorbs retroactive edits without webhooks, which the package does not support. Ship the HarvestGateway abstraction in the same PR.
  3. Week 3 — Contract tests. Capture real payloads as fixtures (one user, one project, one page of entries) and assert DTO hydration under Http::preventStrayRequests(). This is the only defence against Harvest drift.
  4. Week 4+ — Backfill and UI. Chunk historical backfills by month, one job per chunk for retry granularity. Cache users and projects (hourly); never cache time entries — they are the volatile data and the reason the sync exists.
  5. Later — Write paths. Schedule explicitly as a separate epic with its own estimate. Do not let it hide inside "Harvest integration."

Operational Impact

Maintenance. Low steady-state, spiky on Harvest API changes. The package's surface is small enough to read in an hour, so diagnosing a hydration failure is tractable even without upstream fixes. Budget a modest recurring allocation — one engineer-day per quarter — for dependency bumps, fixture refresh, and a coverage audit against the roadmap. The realistic maintenance tail is not "the library breaks"; it is "we slowly fork behaviour into HttpHarvestGateway until the package is a minority of our Harvest code," and that drift should be visible in a tracker rather than discovered during an incident.

Support. No commercial support, no SLA, no vendor escalation path. Internal support is entirely our own, mitigated by MIT and codebase size. The mitigating factor is Spatie's track record; the aggravating factor is that a 0-dependent package has no user community to surface regressions or contribute fixes upstream. Assume we are the maintainer of last resort.

Scaling. Two independent constraints. First, Harvest's per-account rate limits: the correct architecture is sync-into-local-tables with jobs and an overlapping window, never live fan-out from a dashboard — a page that queries Harvest per request will 429 in front of a client. Second, our ingestion volume: time entries are page-yielding, so jobs must iterate pages incrementally and reserve ->collect() for short ranges and interactive use; ->collect() on a multi-month backfill is an unbounded-memory event. Chunk backfills by month, dispatch one job per chunk, and bound every query by date — Harvest requires a from, which is a healthy forcing function. Note also that all() returns plain arrays of DTOs, not Collections; wrap deliberately and check return types rather than assuming Laravel collection semantics.

Failure modes. Enumerated with the operational response:

  • Auth failure (401). Token expiry, token belonging to a different account than HARVEST_ACCOUNT_ID, or a malformed User-Agent. Alert on the smoke-test endpoint; this is the cheapest canary.
  • Rate limiting (429). Symptom of live querying or oversized backfills. Response: backoff with jitter in the job, and a hard architectural rule against synchronous Harvest calls in request handlers.
  • Silent DTO drift. New or renamed Harvest fields hydrate to null with no error. This is the most dangerous failure because it degrades data quality, not availability. Fixture tests plus a periodic field-completeness assertion on the synced tables are the control.
  • Date/timezone skew. Entries land on the wrong day; reconciliations are wrong by 24 hours. Mitigate with a manual spot-check against the Harvest UI for a known day before any downstream billing consumer goes live, and repeat it after any Harvest-side account change.
  • Abandonment. Upstream stops shipping Laravel compatibility releases. Detection is Dependabot on the framework major; response is a fork (small surface, MIT) rather than a replacement evaluation.
  • Missing-endpoint surprise mid-sprint. Cheap to fix (10 minutes), expensive to discover. Mitigate by doing the coverage grep during refinement, not during implementation.

Ramp-up. Genuinely fast — a mid-level Laravel engineer is productive in an afternoon using the README's users/projects/time-entries examples. The ramp-up that matters is not the API, it is the judgement: knowing that this is a partial client, that the DTOs are the real contract, that time entries must be bounded and synced rather than queried live, and that date semantics deserve paranoia. Put those four points in the onboarding doc and the ADR; they are not derivable from the README alone. Overall recommendation: adopt for read-only time-entry and project integration, conditional on the gateway abstraction shipping in the first PR and write operations being scoped as a separate, separately-estimated epic.

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.
ably/pubsub-server
mstfkhazaal/defender
mfonte/base62x
karnoweb/metapilot
tobya/bcsapiwrapper
swalker2/cpanel
vish4395/laravel-translation-manager
morningtrain/economic
teksite/systeminfo-laravel
atomcms/voting
run6/weather
siokas/laravelembeddirectives
adamlien/pkuuids
gilsonsouza/lara-doc-generator
manelgavalda/enrollment_mobile
unabomber/timezone
delatbabel/contacts
astritzeqiri/g-recaptcha
kevinongko/laravel-genode
developeruz/laravel-analytics