← Back to articles

REST API Design That Developers Actually Like

· 3 min read · backend · ... views
Share: Y
On this page

REST isn’t a library. It’s not a framework. It’s a set of conventions for designing web APIs. Follow them and any developer can guess how your API works without reading documentation. Break them and everyone has a bad time.

Resources, Not Actions

The single most important REST principle: think in nouns, not verbs.

RESTful URL Design

Your API exposes “things” (users, posts, orders), not “actions” (createUser, deletePost).

Bad: POST /createUser, GET /getUserById, POST /deleteUser Good: POST /users, GET /users/42, DELETE /users/42

The HTTP method already tells you the action. GET reads. POST creates. PUT replaces. DELETE removes. The URL just identifies WHICH resource.

URL Design That Reads Like English

Good REST URLs tell a story:

  • GET /users/42/posts means “get posts belonging to user 42”
  • POST /teams/5/members means “add a member to team 5”
  • DELETE /orders/99/items/3 means “remove item 3 from order 99”

Conventions:

  • Pluralize resource names: /users not /user
  • Lowercase with hyphens if needed: /user-profiles not /userProfiles
  • Keep nesting shallow (max 2-3 levels)
  • No verbs in URLs ever

Query Parameters for Everything Else

Route params identify WHICH resource. Query params modify HOW you get it:

GET /users?role=admin              filtering
GET /posts?sort=createdAt&order=desc   sorting
GET /products?page=2&limit=20      pagination
GET /users?fields=id,name,email    sparse fields
GET /events?after=2024-01-01       date ranges

Response Formats

Consistent Response Shapes

Be consistent across your entire API. Pick a format and stick with it:

Single resource:

{
  "id": 42,
  "name": "Shubham",
  "email": "shubham@example.com",
  "createdAt": "2024-01-15T10:30:00Z"
}

Collection with pagination:

{
  "data": [...],
  "pagination": {
    "page": 2,
    "limit": 20,
    "total": 156,
    "totalPages": 8
  }
}

Error:

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Invalid input",
    "details": [
      {"field": "email", "message": "must be valid"}
    ]
  }
}

The key: a developer using your API should never be surprised by the shape of a response. Every endpoint follows the same patterns.

Versioning

APIs evolve. You’ll add fields, deprecate endpoints, change behavior. How do you do this without breaking apps that already use your API?

URL versioning (most common, most explicit):

/api/v1/users
/api/v2/users

Start with v1. Only bump the version when you make a BREAKING change. Adding new optional fields is not breaking. Removing a field or changing its type is.

Idempotency: Why It Matters

An operation is idempotent if calling it multiple times gives the same result as calling it once.

  • GET: always idempotent (reading doesn’t change state)
  • PUT: idempotent (replacing with same data = same result)
  • DELETE: idempotent (deleting twice = still deleted)
  • POST: NOT idempotent (creating twice = two resources)

This matters because networks are unreliable. A POST request times out. Did it succeed? The client doesn’t know. If they retry and it’s not idempotent, you get a duplicate. That’s why payment APIs use idempotency keys: “I already processed this exact request, here’s the same result again.”

Wrapping Up

REST is about conventions that make your API predictable:

  • Resources as nouns, methods as verbs
  • Consistent URL structure: /resources/:id
  • Query params for filtering, sorting, pagination
  • Consistent response shapes across all endpoints
  • Version your API with URL prefix
  • Understand idempotency for reliable retries
  • Be predictable. Consistency beats cleverness.

Day 9 of 95 | Backend Engineering Series

Enjoyed this article?
Share: Y

Get new articles in your inbox

No spam. Unsubscribe anytime.

Get in touch

Have a question, feedback, or just want to say hi? Reach out.