The most expensive mistakes in software projects are rarely about code — they are about contracts. An API is a contract written in JSON, and once mobile apps, third-party integrations, or your own frontend start consuming it, every design shortcut becomes a breaking-change negotiation. This article lays out the decisions that keep REST and GraphQL APIs clean, versionable, and painless to evolve.

Start with Resource Modelling, Not Routes

Before writing a single route, model the nouns and the relationships between them. A good resource model makes URL structure predictable: collections, single items, nested relationships, and actions that do not fit CRUD. When an action has no natural noun — sending a password reset, approving an order — model it as a sub-resource under the owning noun rather than a verb in the URL. The model is what survives; routes are merely its public face.

Versioning: Contract Stability First

URL versioning (/v1/orders) is honest and simple to operate, but it spreads version logic into every client. Header-based versioning keeps URLs clean at the cost of discoverability. My recommendation: URL-version the major surface and use Accept-header negotiation for minor evolutions. Whichever you choose, document it in a changelog and enforce a deprecation window — announce a removal at least one release before it lands, and return a structured Deprecation warning header so alert callers can migrate early.

Errors as a Contract, Not an Afterthought

Errors must be as structured as success responses. Adopt RFC 7807 problem+json so every client parses one common envelope: a type URI, a title, a status, and a detail message. Distinguish the four classes that matter — validation failures (422), authorization (403), resource state conflicts (409), and server faults (500) — and expose a machine-readable error code in every payload so frontends branch on logic, not string matching. Never return raw exception traces to clients.

Idempotency and Concurrency

Mobile networks retry. Gateways retry. Payment integrations retry. If your POST /payments is not idempotent, double submission is a data-integrity incident waiting to happen. Accept an Idempotency-Key header, store it against the resource, and return the original response for duplicate keys. For optimistic concurrency, use If-Match with ETags so two dashboards cannot silently overwrite each other. These two mechanisms remove the most common classes of production bugs in resource APIs.

Pagination, Filtering, and Field Selection

Cursor-based pagination is the stable choice for high-throughput data — offsets drift when rows are inserted or deleted mid-page. Return next/prev cursors in the response envelope so clients stay stateless. Filtering should use an explicit whitelist of keys with documented operators; never reflect a raw query string into SQL. Provide sparse field selection, and prefer returning slightly more than clients need over dumping entire tables. Every extra column is a new serialization cost and a new breaking-change surface.

GraphQL: Schema Design and the N+1 Trap

GraphQL solves client flexibility and creates two new problems: unbounded query depth and the N+1 query explosion. Design the schema around use cases, not database tables. Batch every resolver with a DataLoader-style pattern so a list of ten items issues one query, not eleven. Enforce query cost limits, cap depth, and use persisted queries for production apps so clients cannot craft pathological requests at runtime.

One REST + GraphQL Strategy

  • Expose the same underlying services through a gateway so logic is written once.
  • Document both interfaces from a single OpenAPI/SDL source of truth.
  • Rate-limit by API key and token, with clear 429 envelopes and Retry-After.
  • Log the API version, client, and correlation ID on every request.
  • Run contract tests against the documented schema before every release.

A clean API is a quiet one: it never surprises, fails predictably, and evolves without fanfare. Smart Logic designs and builds REST and GraphQL backends — including Laravel and PHP services — with these contracts baked in from the first schema. If you are about to expose your first public API or need to rebuild an unstable one, talk to us about an API architecture review before the contract hardens.