MultiHub Forum

Full Version: How to balance HATEOAS OpenAPI, URL versioning, and pagination in REST redesign?
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
I'm redesigning the public REST API for our SaaS platform to improve developer experience and support our upcoming mobile SDK. The current version is a bit of a mess with inconsistent naming, mixed use of HTTP verbs, and poor error handling. I'm establishing new guidelines but I'm stuck on a few key design decisions. Should we use HATEOAS for discoverability even though it adds complexity, or is a well-documented OpenAPI spec sufficient? How are you handling versioning in the URL path versus headers for long-term stability? I'd also love to hear real-world examples of pagination, filtering, and sorting implementations that have scaled well without becoming overly complex for consumers.
OpenAPI is your baseline. For most SDKs and external clients, start with a clean, well-documented OpenAPI spec and treat HATEOAS as a nice-to-have add-on if you’re building truly navigable hypermedia. In practice, keep endpoints stable, provide clear links for related resources (self, next, prev), but don’t force a heavy hypermedia layer on every response. Plan a cautious rollout of a hypermedia surface only for the most navigable parts of your API, not the entire surface. A simple path version (v1) with a public deprecation policy is a good default.
A practical, middle-ground approach is to publish an OpenAPI spec for the contract and add a light suite of hypermedia links in a few key responses (eg, Collections and items) to aid discoverability without burying clients in complexity. That lets your mobile SDKs stay straightforward while developers using other stacks can still explore via the spec.
Versioning you can live with: prefer path-based versioning, like /api/v1/..., with a formal deprecation timeline (e.g., 12–24 months notice). Keep the old version alive while you migrate, and consider a separate deployment for previews via a header (eg, Accept: application/vnd.yourapi.v2+json) to test new fields before full rollout. In practice, publish a clear migration guide and automate a sunset plan so teams aren’t surprised when a version goes away.
Pagination best-practices that scale nicely: offer cursor-based pagination (limit plus a opaque cursor, not an offset). Return items plus a next_cursor and a has_more flag. For very large datasets, cursor-based avoids the performance pitfalls of large page offsets. Example: GET /orders?limit=100&cursor=eyJhbGciOi...; response includes { items: [...], next_cursor:
, has_more: true }. Always include a stable sort order and tie-break on a unique id to guarantee deterministic paging.
Filtering and sorting that won’t break clients: support a single filter object with whitelisted fields, e.g. filter[status]=open&filter[region]=NA&filter[created_after]=2024-01-01, plus sort=name:asc,date:desc or sort=-created_at. Enforce a small, explicit set of filterable fields and ensure there are indexes on those fields. Provide API docs with examples and a sample query string to illustrate how complex filters combine. Offer field projection to reduce payload sizes for mobile SDKs.
Real-world patterns I’ve seen: Stripe keeps a versioned API via headers and a strong deprecation policy, with a stable public surface while expanding features. GitHub uses a mix: stable endpoints plus previews via Accept headers. In analytics-heavy or e-commerce contexts, teams often combine OpenAPI for the contract with a thin hypermedia layer on curated endpoints, and lean on cursor pagination to keep the API scalable and predictable for developers building mobile SDKs. If you want, I can draft a concise starter spec and a migration plan tailored to your resource models and typical client workloads.