Product Decisions This Supports
- API representation strategy — route-level and path-based negotiation make "which formats does this endpoint speak?" an explicit, per-route product decision rather than an accident of controller code. That is the artifact you need when deciding whether v1 of a public API ships JSON only or JSON + XML + CSV.
- Versioning and deprecation roadmap — negotiation is the mechanism behind
Accept: application/vnd.acme.v2+json and /v2/ style access. Choosing this package now is effectively pre-committing to a negotiation-based versioning story, which shapes how you announce and retire endpoints later.
- Client-segment prioritization — path-based negotiation (
.json, .csv, extensions/prefixes) is what makes "let partners pull the same resource as a spreadsheet" a one-line ask instead of a sprint. This directly informs whether self-serve data export is a roadmap item or a bespoke integration project.
- Build vs. buy — the alternative is hand-rolled middleware plus per-controller branching. Two negotiation modes out of the box (route-level, path-based) is a credible "buy" signal for teams that would otherwise spend a week reinventing a partial version.
- Platform vs. product team boundary — route-level configuration means product teams declare intent in their route files while the platform team owns the resolution behavior. Useful when arguing for a shared API conventions layer rather than per-team snowflakes.
- Use cases unlocked — partner/marketplace APIs, mobile clients pinning a representation version, public data endpoints with CSV/JSON parity, and gradual migration off a legacy XML surface.
When to Consider This Package
Consider it when most of the following hold:
- You expose (or are about to expose) an API to parties you don't control — partners, marketplace integrators, public developers — where
Accept-header correctness is table stakes rather than pedantry.
- You need more than one representation of the same resource and want that declared at the routing layer, not scattered across controllers, formatters, and
if ($request->wantsJson()) branches.
- You are on Laravel and want negotiation to compose with existing middleware, route groups, and API Resources rather than replacing them.
- You need path-based access (extension or prefix) because real clients — curl scripts, spreadsheet imports, BI tools — are bad at setting headers.
- You want the decision documented in code so a future team can see which endpoints promised which formats.
Look elsewhere, or defer, when:
- Maturity risk is unacceptable. This is v0.1.0. Two features, no 1.0, no implied backwards-compatibility guarantee. Treat the public surface as unstable until a 1.0 lands or you've pinned and wrapped it.
- You have a single-representation internal API. Negotiation infrastructure with one option is pure carrying cost.
- Your real problem is serialization, not negotiation. If you're choosing between formats for the same object graph, Laravel API Resources, Fractal, or Symfony Serializer do more of the actual work; this package decides which serializer runs, not how.
- You need gateway-level concerns — rate limiting, auth, caching keys per representation, HATEOAS — handled coherently. Negotiation is one layer; don't mistake it for an API platform.
- You're on GraphQL, gRPC, or a single-client SPA where the answer is always JSON. There is no negotiation to perform.
- You need vendor-backed support or a security review trail. Pre-1.0 single-maintainer packages rarely provide either.
Adoption guidance if you proceed: pin the exact version, wrap it behind your own thin abstraction or base controller so an API change is a one-file edit, and add contract tests asserting the negotiated output for each declared route.
How to Pitch It (Stakeholders)
For executives: This is a small, early-stage open-source component that lets our API serve the same data in multiple formats and versions, declared per endpoint. It answers a question we will face the moment we sign a partner that needs CSV or a pinned API version, and it answers it cheaply. The honest caveat: it is a v0.1.0 with two features and no stability guarantee, so we should treat it as a low-cost bet we can reverse in a day, not infrastructure we build a commitment on. Recommended posture: adopt behind a thin internal wrapper, revisit at 1.0.
For engineering: Route-level negotiation gives each route an explicit declaration of supported representations; path-based negotiation covers clients that can't set headers (extensions/prefixes). It slots in as middleware and composes with route groups and API Resources rather than competing with them. The costs are the usual pre-1.0 costs — unstable API surface, limited feature set, unclear maintenance runway — mitigated by pinning the version and isolating it behind our own resolution layer with contract tests per route. If we outgrow it, the wrapper is the seam we replace, and our route declarations survive the swap.