Profil Resmi

Mustafacan Akdoğan

Software Architect
Coding...

Insight

+100%
1
Blog Post
+0%
0
Cheatsheet
System Online

Mustafacan Akdoğan

Software Architect

Back /Laravel API Fundamentals That Matter
PHPLaravel

Laravel API Fundamentals That Matter

0.0
0
2026.02.26

Most APIs fail, not because the code is bad, but because the decisions are shallow.
This is a decision-first breakdown of the API choices in this project, mapped to trade-offs, failures, and edge-cases.

🔗 Reference implementationm: https://github.com/mustafacanakdogan/laravel-api-essentials


1) API Versioning

  • Decision: Version the API (/v1, /v2) instead of breaking existing clients.
  • Trade-off: More maintenance overhead, because multiple versions may need to run in parallel.
  • Implementation choice: Route groups per version, with shared response and error handling.
  • Failure scenario: A client pinned to an older version breaks after a hidden change.
  • Edge-case: Endpoints added to v2 but not v1 must be clearly documented.

2) Unified Response Contract

  • Decision: All responses include success, data/error, and meta.
  • Trade-off: Slightly larger payloads.
  • Implementation choice: Central response helpers enforce a single schema.
  • Failure scenario: A client fails to parse inconsistent errors or success payloads.
  • Edge-case: Success responses without payload still need a consistent envelope. In this project, I return 200 with a standard body instead of relying on empty 204 responses.

3) Token-based Auth

  • Decision: Use stateless auth with token issuance and revoke support.
  • Trade-off: Revocation requires token tracking.
  • Implementation choice: Laravel Sanctum with revoke endpoints.
  • Failure scenario: A compromised token continues to work until explicitly revoked.
  • Edge-case: Multiple active devices require a revoke-all flow.

4) Rate Limiting

  • Decision: Throttle both auth attempts and API usage.
  • Trade-off: Legitimate burst traffic can be rate-limited.
  • Implementation choice: IP + email limiter for auth, user + IP + route limiter for API access.
  • Failure scenario: Credential stuffing overwhelms the auth surface.
  • Edge-case: Shared IPs (NAT) can create false positives, so user-scoped keys matter.

5) Idempotency for Writes

  • Decision: Make write operations explicitly idempotent.
  • Trade-off: Extra cache storage and lookup overhead.
  • Implementation choice: Redis-backed idempotency keys with a 24-hour TTL.
  • Failure scenario: Payment creation is retried after a timeout and causes duplicate side effects.
  • Edge-case: The same idempotency key may be reused by mistake. The important property is determinism: repeated writes should not trigger duplicate side effects.

6) Cursor Pagination

  • Decision: Use cursor pagination for large datasets.
  • Trade-off: Harder to inspect manually; no direct page numbers.
  • Implementation choice: Cursor values returned via meta.cursor.next/prev.
  • Failure scenario: Offset pagination drifts when rows are inserted or deleted between requests.
  • Edge-case: Clients must store cursors correctly, so the docs must be explicit.

7) Observability (Logs + Dashboards)

  • Decision: Use structured logs with pre-wired dashboards.
  • Trade-off: Slight overhead in logging and storage.
  • Implementation choice: request.completed logs with request_id, status, and duration_ms.
  • Failure scenario: A latency spike appears in production and nobody can locate the affected path fast enough.
  • Edge-case: If logs are not JSON, dashboards and filters break; that is why the formatter is structured.

8) OpenAPI + Redoc

  • Decision: Treat OpenAPI as a product artifact.
  • Trade-off: The spec needs ongoing maintenance.
  • Implementation choice: A static spec aligned with the real routes and exposed through Redoc.
  • Failure scenario: A client relies on undocumented behavior and breaks after an implementation change.
  • Edge-case: Missing endpoints in the spec create a reliability gap even if the API itself works.

Failure & Resilience Notes

  • Retry behavior: Idempotency keys make write retries safe.
  • Idempotency TTL: 24 hours balances retry safety and storage cost.
  • Metrics exposure: /metrics is auth-protected to avoid leaking internal signals.
  • Slow endpoint purpose: /slow is intentionally included to validate latency monitoring and dashboard behavior under controlled delay scenarios.

A Real Production Incident

We once had a payment retry storm after a gateway timeout.
Without idempotency, retries created duplicate charges and required manual refunds.
With idempotency keys, the same retries returned a deterministic result and no duplicate side-effects happened.


Metric Reasoning: p95 vs Avg

Average latency hides tail pain.
A service can show 150ms avg while p95 is 2–3 seconds, and users still experience the system as slow.
That is why dashboards should expose percentiles such as p95 and p99 instead of relying only on averages.


What I Would Improve Next

  • Prometheus metrics endpoint for native percentile tracking.
  • Request sampling + traces for deeper latency attribution.
  • Rate limit headers for client-side backoff strategy.
  • Automated contract tests that validate OpenAPI against runtime behavior.

Final Thought

You do not get resilience by writing more code.
You get resilience by designing for failure before it happens.