REST API

An architectural style for building web APIs using HTTP methods and stateless communication.

REST (Representational State Transfer) is an architectural style for building web APIs. It's not a protocol or standard - it's a set of constraints that, when followed, create APIs that are scalable, simple, and easy to understand. Most public APIs you interact with (Twitter, GitHub, Stripe) are REST APIs.

Core Principles

REST isn't just "use HTTP" - it has specific constraints that make APIs predictable:

1. Stateless Each request contains all information needed to process it. The server doesn't remember previous requests. This means you can't say "get the next page" - you must say "get page 2 of users sorted by name". This makes APIs scalable because any server can handle any request.

2. Client-Server Separation The client (frontend) and server (API) evolve independently. The server doesn't care if you're using React, mobile app, or curl. The client doesn't care if the server uses PostgreSQL or MongoDB.

3. Uniform Interface Every REST API works the same way: resources have URLs, you use HTTP methods to interact with them, and responses are self-descriptive. Once you learn one REST API, you can use any REST API.

4. Resource-Based Everything is a resource with a unique URL. Users, products, orders - each has an address. You don't call functions like getUser() - you access resources like GET /users/123.

HTTP Methods

MethodPurposeIdempotentExample
GETRead dataYesGET /users - list users
POSTCreate new resourceNoPOST /users - create user
PUTReplace entire resourceYesPUT /users/1 - replace user 1
PATCHUpdate partial resourceNoPATCH /users/1 - update user 1's email
DELETERemove resourceYesDELETE /users/1 - delete user 1

Idempotent means calling it multiple times has the same effect as calling once. DELETE /users/1 three times still results in user 1 being deleted. But POST /users three times creates three users.

URL Design

Good REST URLs are intuitive and consistent:

PatternExampleDescription
Collection/usersAll users
Single resource/users/123User with ID 123
Nested resource/users/123/ordersOrders belonging to user 123
Filtered collection/users?status=activeActive users only

Naming conventions:

  • Use nouns, not verbs: /users not /getUsers
  • Use plural: /products not /product
  • Use kebab-case: /user-profiles not /userProfiles
  • Keep it flat when possible: max 2-3 levels of nesting

Status Codes

The response status code tells the client what happened:

CodeMeaningWhen to Use
200OKSuccessful GET, PUT, PATCH
201CreatedSuccessful POST that created resource
204No ContentSuccessful DELETE
400Bad RequestInvalid input from client
401UnauthorizedMissing or invalid authentication
403ForbiddenAuthenticated but not allowed
404Not FoundResource doesn't exist
409ConflictResource already exists (duplicate email)
422UnprocessableValid syntax but semantic error
500Server ErrorBug in your code

Common Mistakes

1. Using verbs in URLsPOST /createUser or GET /getUsersPOST /users and GET /users

2. Not using proper status codes ❌ Returning 200 with {error: "Not found"} ✅ Returning 404 with proper error body

3. Inconsistent naming/users, /product, /OrderItems/users, /products, /order-items

4. Deeply nested URLs/users/1/orders/2/items/3/reviews/4/order-items/3/reviews or /reviews?order_item=3

5. Ignoring idempotency PUT should replace the entire resource. If client sends {name: "John"}, don't keep the old email - that's what PATCH is for.

Best Practices

Use consistent response format: Always return the same structure. If GET /users/1 returns {id, name, email}, then POST /users should return the same shape.

Include pagination for lists: Never return unbounded lists. Use ?page=1&limit=20 or cursor-based pagination.

Version your API: Use /v1/users or Accept: application/vnd.api.v1+json. When you need breaking changes, create v2.

Return created/updated resources: After POST or PUT, return the full resource. The client needs the server-generated ID and timestamps.

Use HATEOAS for discoverability: Include links to related resources: {id: 1, name: "John", links: {orders: "/users/1/orders"}}

Code Examples

Complete REST API Example

# List all users (with pagination)
GET /api/v1/users?page=1&limit=20
Authorization: Bearer <token>

# Response: 200 OK
{
  "data": [{"id": 1, "name": "John", "email": "john@example.com"}],
  "meta": {"page": 1, "total": 45}
}

# Create a new user
POST /api/v1/users
Content-Type: application/json

{"name": "Jane", "email": "jane@example.com"}

# Response: 201 Created
{"id": 2, "name": "Jane", "email": "jane@example.com", "created_at": "..."}

# Update user's email only
PATCH /api/v1/users/2
Content-Type: application/json

{"email": "jane.doe@example.com"}

# Delete a user
DELETE /api/v1/users/2

# Response: 204 No Content