--- title: "REST API Design: Mistakes I've Made So You Don't Have To" date: 2026-05-13T09:00:00+08:00 draft: false tags: - API - REST - Backend --- I've designed, built, and maintained enough REST APIs to have made most of the classic mistakes. Here's what I wish someone had told me upfront. ## 1. Versioning Early Don't wait until you need it. Version your API from day one, either in the URL (`/v1/users`) or via the `Accept` header. Retrofitting versioning onto an unversioned API is painful for everyone involved. My preference: **URL-based versioning**. It's visible, impossible to forget, and trivially cacheable. ## 2. Pagination by Default Every list endpoint should be paginated. Every. Single. One. ```json { "data": [...], "pagination": { "cursor": "eyJpZCI6MTIzfQ==", "has_more": true, "total": 847 } } ``` I've moved from offset-based to cursor-based pagination almost everywhere. Cursors handle real-time data changes gracefully and perform better on large datasets. ## 3. Error Responses Should Be Consistent A predictable error shape means less client-side code: ```json { "error": { "code": "INSUFFICIENT_BALANCE", "message": "Account balance too low for this transaction", "details": { "required": 29.99, "available": 12.50 } } } ``` Never expose stack traces in production. Never leak internal IDs without intent. ## 4. Idempotency Keys For any mutating endpoint, support an `Idempotency-Key` header. Your payment team will thank you, and so will your users when a retry doesn't double-charge them. ## 5. Think in Terms of Deprecation Add a `Deprecation` and `Sunset` header to old endpoints: ``` Deprecation: true Sunset: Sat, 01 Aug 2026 00:00:00 GMT ``` Give consumers time to migrate, then actually follow through on removal. Keeping deprecated endpoints around "just in case" is technical debt with interest. --- Good API design is mostly about **empathy for the consumer**. Ask yourself: would *you* enjoy integrating with this?