74 lines
2.2 KiB
Markdown
74 lines
2.2 KiB
Markdown
---
|
|
title: "REST API Design: Mistakes I've Made So You Don't Have To"
|
|
date: 2026-05-13T09:00:00+08:00
|
|
draft: false
|
|
description: "Lessons from real-world API design. Why version early, paginate everything, standardize error responses, and add idempotency keys. Plus a practical deprecation strategy with Sunset headers."
|
|
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?
|