Back to blog

What is REST API? Complete Guide for Developers

rest-apiapibackendweb-developmenthttp
What is REST API? Complete Guide for Developers

APIs power the modern web. Every time you check the weather on your phone, scroll through social media, or make an online payment, you're using APIs. Among all API architectures, REST has become the de facto standard for web services.

In this comprehensive guide, you'll learn everything you need to know about REST APIs—from fundamental concepts to advanced patterns used in production systems.

What You'll Learn

✅ What APIs are and why they matter
✅ REST architectural principles and constraints
✅ HTTP methods and when to use each one
✅ Status codes and their meanings
✅ URL design and resource naming conventions
✅ Authentication and authorization methods
✅ Versioning, pagination, and filtering strategies
✅ Best practices for building robust REST APIs

Prerequisites

Before diving in, you should have:


Part 1: Understanding APIs

What is an API?

API stands for Application Programming Interface. It's a contract that defines how two software applications communicate with each other.

Think of an API like a restaurant menu:

  • You (client): Want to order food
  • Menu (API): Shows what's available and how to order
  • Kitchen (server): Prepares and delivers what you ordered
  • Waiter (API request/response): Carries your order and brings back food
┌─────────────┐                      ┌─────────────┐
│   Client    │  ──── Request ────▶  │   Server    │
│  (Your App) │                      │  (Backend)  │
│             │  ◀─── Response ────  │             │
└─────────────┘                      └─────────────┘

Why Do We Need APIs?

APIs solve several critical problems:

1. Separation of Concerns

  • Frontend and backend can evolve independently
  • Mobile apps, web apps, and third-party services all use the same backend

2. Interoperability

  • Different technologies can communicate (Java backend → JavaScript frontend)
  • Services from different companies can integrate

3. Reusability

  • Build once, consume everywhere
  • The same API serves mobile apps, web apps, IoT devices, and partners

4. Security

  • Control exactly what data and operations are exposed
  • Implement authentication and authorization at the API layer

Types of APIs

Before focusing on REST, let's see the API landscape:

TypeFormatUse CaseExample
RESTJSON/XML over HTTPWeb services, mobile backendsTwitter API, GitHub API
GraphQLJSON over HTTPFlexible queries, mobile appsFacebook, Shopify
gRPCProtocol BuffersMicroservices, real-timeGoogle, Netflix
SOAPXMLEnterprise, legacy systemsBanking, healthcare
WebSocketBinary/TextReal-time bidirectionalChat apps, gaming

REST dominates web APIs because of its simplicity and ubiquity.


Part 2: REST Fundamentals

What is REST?

REST stands for Representational State Transfer. It was introduced by Roy Fielding in his 2000 doctoral dissertation.

REST is not a protocol or standard—it's an architectural style with a set of constraints that, when followed, creates scalable and maintainable web services.

The Six REST Constraints

For an API to be truly "RESTful," it must follow these constraints:

1. Client-Server

The client and server are separate concerns:

  • Client: Handles user interface and user experience
  • Server: Handles data storage, business logic, and security

This separation allows each to evolve independently.

┌─────────────┐         ┌─────────────┐
│   Client    │◀───────▶│   Server    │
│  (React UI) │   HTTP  │  (Node.js)  │
└─────────────┘         └─────────────┘
       ▲                       ▲
       │                       │
   UI Logic              Business Logic
   Presentation          Data Storage

2. Stateless

Each request must contain all information needed to process it. The server doesn't store client state between requests.

// ❌ Stateful (server remembers user)
GET /api/cart  // Server: "Which user? I need to check session..."
 
// ✅ Stateless (request contains everything)
GET /api/users/123/cart
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...

Benefits of statelessness:

  • Easier to scale (any server can handle any request)
  • Simpler to debug (each request is self-contained)
  • Better reliability (server restarts don't affect clients)

3. Cacheable

Responses must indicate whether they can be cached:

HTTP/1.1 200 OK
Cache-Control: max-age=3600, public
ETag: "abc123"
 
{
  "products": [...]
}

Caching improves performance and reduces server load.

4. Uniform Interface

This is the most important constraint. REST APIs must have a consistent, predictable interface:

  • Resource identification: Use URIs to identify resources
  • Resource manipulation: Use HTTP methods (GET, POST, PUT, DELETE)
  • Self-descriptive messages: Include metadata in headers
  • HATEOAS: Include links to related resources (more on this later)

5. Layered System

Clients can't tell whether they're connected directly to the server or through intermediaries (load balancers, caches, proxies):

Client → CDN → Load Balancer → API Gateway → Server

This enables:

  • Load balancing for scalability
  • Caching at multiple levels
  • Security (firewalls, WAF)

6. Code on Demand (Optional)

Servers can optionally send executable code to clients (JavaScript). This is the only optional constraint and rarely used in practice.

Resources: The Heart of REST

In REST, everything is a resource. A resource is any information that can be named:

  • A user
  • A blog post
  • An order
  • A collection of products

Each resource has:

  • A unique identifier (URI)
  • One or more representations (JSON, XML, HTML)
Resource: User #123
URI: /api/users/123
Representation (JSON):
{
  "id": 123,
  "name": "John Doe",
  "email": "john@example.com"
}

Part 3: HTTP Methods in Depth

HTTP methods (also called "verbs") define what action to perform on a resource.

The CRUD Mapping

HTTP MethodCRUD OperationSQL Equivalent
POSTCreateINSERT
GETReadSELECT
PUTUpdate (full)UPDATE
PATCHUpdate (partial)UPDATE
DELETEDeleteDELETE

GET - Retrieve Resources

Purpose: Fetch a resource or collection of resources.

Characteristics:

  • Safe (doesn't modify data)
  • Idempotent (same request = same result)
  • Cacheable
# Get a single user
GET /api/users/123 HTTP/1.1
Host: api.example.com
Accept: application/json
 
# Response
HTTP/1.1 200 OK
Content-Type: application/json
 
{
  "id": 123,
  "name": "John Doe",
  "email": "john@example.com",
  "createdAt": "2024-01-15T10:30:00Z"
}
# Get a collection with filtering and pagination
GET /api/users?role=admin&page=1&limit=10 HTTP/1.1
 
# Response
HTTP/1.1 200 OK
Content-Type: application/json
 
{
  "data": [
    {"id": 1, "name": "Alice", "role": "admin"},
    {"id": 2, "name": "Bob", "role": "admin"}
  ],
  "pagination": {
    "page": 1,
    "limit": 10,
    "total": 2,
    "totalPages": 1
  }
}

POST - Create Resources

Purpose: Create a new resource.

Characteristics:

  • Not safe (modifies data)
  • Not idempotent (each call creates a new resource)
  • Not cacheable
# Create a new user
POST /api/users HTTP/1.1
Host: api.example.com
Content-Type: application/json
 
{
  "name": "Jane Doe",
  "email": "jane@example.com",
  "password": "securepassword123"
}
 
# Response
HTTP/1.1 201 Created
Location: /api/users/124
Content-Type: application/json
 
{
  "id": 124,
  "name": "Jane Doe",
  "email": "jane@example.com",
  "createdAt": "2024-01-20T14:22:00Z"
}

Note: The Location header tells the client where to find the new resource.

PUT - Replace Resources

Purpose: Replace an entire resource with a new representation.

Characteristics:

  • Not safe (modifies data)
  • Idempotent (same request produces same result)
  • Requires the complete resource
# Replace user 123 entirely
PUT /api/users/123 HTTP/1.1
Content-Type: application/json
 
{
  "name": "John Smith",
  "email": "john.smith@example.com",
  "role": "admin"
}
 
# Response
HTTP/1.1 200 OK
Content-Type: application/json
 
{
  "id": 123,
  "name": "John Smith",
  "email": "john.smith@example.com",
  "role": "admin",
  "updatedAt": "2024-01-20T15:00:00Z"
}

Important: PUT replaces the entire resource. Any fields not included in the request body may be set to null or default values.

PATCH - Partial Update

Purpose: Apply partial modifications to a resource.

Characteristics:

  • Not safe (modifies data)
  • Can be idempotent (depends on implementation)
  • Only updates specified fields
# Update only the email
PATCH /api/users/123 HTTP/1.1
Content-Type: application/json
 
{
  "email": "newemail@example.com"
}
 
# Response
HTTP/1.1 200 OK
Content-Type: application/json
 
{
  "id": 123,
  "name": "John Smith",
  "email": "newemail@example.com",
  "role": "admin",
  "updatedAt": "2024-01-20T15:30:00Z"
}

PUT vs PATCH

AspectPUTPATCH
PayloadComplete resourcePartial updates only
Missing fieldsSet to null/defaultUnchanged
IdempotentYesUsually yes
Use caseFull replacementTargeted updates
// PUT: Must include ALL fields
PUT /api/users/123
{
  "name": "John",
  "email": "john@example.com",
  "role": "user",
  "phone": null,
  "address": null
}
 
// PATCH: Only include what changes
PATCH /api/users/123
{
  "role": "admin"
}

DELETE - Remove Resources

Purpose: Remove a resource.

Characteristics:

  • Not safe (modifies data)
  • Idempotent (deleting twice has same effect as once)
# Delete user 123
DELETE /api/users/123 HTTP/1.1
Host: api.example.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
 
# Response options:
 
# Option 1: 204 No Content (preferred)
HTTP/1.1 204 No Content
 
# Option 2: 200 OK with deleted resource
HTTP/1.1 200 OK
Content-Type: application/json
 
{
  "id": 123,
  "name": "John Smith",
  "deleted": true,
  "deletedAt": "2024-01-20T16:00:00Z"
}

HEAD - Check Resource Existence

Purpose: Same as GET but returns only headers (no body).

# Check if resource exists
HEAD /api/users/123 HTTP/1.1
 
# Response
HTTP/1.1 200 OK
Content-Length: 256
Last-Modified: Sat, 20 Jan 2024 15:30:00 GMT
ETag: "abc123"

Use cases:

  • Check if resource exists before downloading
  • Get metadata without transferring body
  • Validate cache

OPTIONS - Discover Capabilities

Purpose: Describe the communication options for a resource.

# What methods are allowed?
OPTIONS /api/users HTTP/1.1
 
# Response
HTTP/1.1 200 OK
Allow: GET, POST, HEAD, OPTIONS
Access-Control-Allow-Methods: GET, POST, PUT, DELETE

Commonly used in CORS preflight requests.

Method Safety and Idempotency Summary

MethodSafeIdempotentRequest BodyResponse Body
GET
HEAD
OPTIONS
POST
PUT
PATCHDepends
DELETEOptional
  • Safe: Doesn't modify server state
  • Idempotent: Multiple identical requests have the same effect as one

Part 4: HTTP Status Codes

Status codes tell clients what happened with their request. They're grouped by the first digit:

1xx - Informational

Rarely used in REST APIs.

CodeNameUse Case
100ContinueLarge upload in progress
101Switching ProtocolsUpgrade to WebSocket

2xx - Success

The request succeeded.

CodeNameWhen to Use
200OKGET, PUT, PATCH succeeded
201CreatedPOST created new resource
202AcceptedAsync operation started
204No ContentDELETE succeeded, no body
# 200 OK - Resource retrieved
GET /api/users/123
→ 200 OK with user data
 
# 201 Created - New resource created
POST /api/users
→ 201 Created with new user + Location header
 
# 202 Accepted - Async job started
POST /api/reports/generate
→ 202 Accepted with job ID
 
# 204 No Content - Deleted successfully
DELETE /api/users/123
→ 204 No Content

3xx - Redirection

The client needs to take additional action.

CodeNameWhen to Use
301Moved PermanentlyResource URL changed forever
302FoundTemporary redirect
304Not ModifiedCache is still valid
307Temporary RedirectTemporary redirect (preserve method)
308Permanent RedirectPermanent redirect (preserve method)
# 301 Moved Permanently
GET /api/v1/users
→ 301 Moved Permanently
   Location: /api/v2/users
 
# 304 Not Modified (caching)
GET /api/users/123
If-None-Match: "abc123"
→ 304 Not Modified

4xx - Client Errors

The client made an error.

CodeNameWhen to Use
400Bad RequestInvalid request syntax/body
401UnauthorizedAuthentication required
403ForbiddenAuthenticated but not authorized
404Not FoundResource doesn't exist
405Method Not AllowedHTTP method not supported
409ConflictResource state conflict
422Unprocessable EntityValidation failed
429Too Many RequestsRate limit exceeded
# 400 Bad Request - Malformed JSON
POST /api/users
Content-Type: application/json
{ "name": "John", }  ← trailing comma
 
400 Bad Request
{
  "error": "Bad Request",
  "message": "Invalid JSON in request body"
}
 
# 401 Unauthorized - Missing or invalid token
GET /api/users/123
Authorization: Bearer invalid_token
 
401 Unauthorized
{
  "error": "Unauthorized",
  "message": "Invalid or expired token"
}
 
# 403 Forbidden - User can't access resource
DELETE /api/users/1  ← Admin trying to delete super admin
 
403 Forbidden
{
  "error": "Forbidden",
  "message": "Cannot delete super admin users"
}
 
# 404 Not Found - Resource doesn't exist
GET /api/users/99999
 
404 Not Found
{
  "error": "Not Found",
  "message": "User with id 99999 not found"
}
 
# 409 Conflict - State conflict
POST /api/users
{ "email": "existing@example.com" }
 
409 Conflict
{
  "error": "Conflict",
  "message": "User with this email already exists"
}
 
# 422 Unprocessable Entity - Validation failed
POST /api/users
{ "email": "not-an-email", "age": -5 }
 
422 Unprocessable Entity
{
  "error": "Validation Error",
  "details": [
    {"field": "email", "message": "Invalid email format"},
    {"field": "age", "message": "Age must be positive"}
  ]
}
 
# 429 Too Many Requests - Rate limited
GET /api/users
 
429 Too Many Requests
Retry-After: 60
{
  "error": "Too Many Requests",
  "message": "Rate limit exceeded. Try again in 60 seconds"
}

5xx - Server Errors

Something went wrong on the server.

CodeNameWhen to Use
500Internal Server ErrorUnexpected server error
502Bad GatewayUpstream server error
503Service UnavailableServer overloaded/maintenance
504Gateway TimeoutUpstream server timeout
# 500 Internal Server Error - Catch-all for bugs
GET /api/users
 
→ 500 Internal Server Error
{
  "error": "Internal Server Error",
  "message": "An unexpected error occurred",
  "requestId": "req_abc123" for debugging
}
 
# 503 Service Unavailable - Planned maintenance
GET /api/users
 
503 Service Unavailable
Retry-After: 3600
{
  "error": "Service Unavailable",
  "message": "Scheduled maintenance in progress"
}

Status Code Decision Tree

Request received
├── Is the request valid?
│   ├── No → 400 Bad Request
│   └── Yes ↓
├── Is authentication required?
│   ├── Yes, and missing/invalid → 401 Unauthorized
│   └── Has valid auth ↓
├── Is user authorized for this action?
│   ├── No → 403 Forbidden
│   └── Yes ↓
├── Does the resource exist?
│   ├── No → 404 Not Found
│   └── Yes ↓
├── Is the method allowed?
│   ├── No → 405 Method Not Allowed
│   └── Yes ↓
├── Is there a conflict?
│   ├── Yes → 409 Conflict
│   └── No ↓
├── Is the data valid?
│   ├── No → 422 Unprocessable Entity
│   └── Yes ↓
├── Did the server crash?
│   ├── Yes → 500 Internal Server Error
│   └── No ↓
└── Success! → 2xx (200, 201, 204)

Part 5: URL Design and Resource Naming

Good URL design makes your API intuitive and self-documenting.

URL Structure

https://api.example.com/v1/users/123/orders?status=pending&limit=10
└─┬─┘   └─────┬─────┘ └┬┘ └────┬─────────┘ └────────┬───────────┘
scheme      host    version  path (resource)    query params

Resource Naming Conventions

1. Use Nouns, Not Verbs

Resources are things, not actions.

✅ Good (nouns)
GET /users
POST /orders
DELETE /products/123
 
❌ Bad (verbs)
GET /getUsers
POST /createOrder
DELETE /deleteProduct/123

2. Use Plural Nouns

Consistency is key—always use plurals.

✅ Good (plural)
/users
/users/123
/products
/products/456/reviews
 
❌ Bad (inconsistent)
/user          ← singular
/users/123     ← plural
/product/456

3. Use Hyphens for Multi-Word Resources

✅ Good
/user-profiles
/order-items
/blog-posts
 
❌ Bad
/user_profiles     ← underscores
/userprofiles      ← no separator
/UserProfiles      ← PascalCase

4. Use Lowercase

URLs are case-sensitive in many servers.

✅ Good
/users/123/orders
 
❌ Bad
/Users/123/Orders
/USERS/123/ORDERS

5. Represent Relationships with Nesting

Show parent-child relationships in the URL.

# User's orders
GET /users/123/orders
 
# Order's items
GET /orders/456/items
 
# Specific item in an order
GET /orders/456/items/789

But don't nest too deep (max 2-3 levels):

❌ Too deep
/users/123/orders/456/items/789/variants/321
 
✅ Better - flatten when needed
/order-items/789?include=variants

Query Parameters

Use query parameters for:

Filtering

GET /products?category=electronics&price_min=100&price_max=500
GET /users?role=admin&status=active
GET /orders?created_after=2024-01-01

Sorting

GET /products?sort=price         # ascending
GET /products?sort=-price        # descending (prefix with -)
GET /products?sort=category,-price  # multiple fields

Pagination

# Offset-based
GET /products?page=2&limit=20
 
# Cursor-based
GET /products?cursor=abc123&limit=20

Field Selection

GET /users?fields=id,name,email
GET /users/123?fields=name,avatar

Expansion/Inclusion

GET /orders/123?include=items,customer
GET /posts?include=author,comments

URL Patterns Cheat Sheet

ActionHTTP MethodURL PatternExample
List allGET/resourcesGET /users
Get oneGET/resources/:idGET /users/123
CreatePOST/resourcesPOST /users
ReplacePUT/resources/:idPUT /users/123
UpdatePATCH/resources/:idPATCH /users/123
DeleteDELETE/resources/:idDELETE /users/123
Nested listGET/resources/:id/subresourcesGET /users/123/orders
Nested oneGET/resources/:id/subresources/:subIdGET /users/123/orders/456

Non-Resource Endpoints

Sometimes you need endpoints that don't fit the resource model:

# Actions on resources
POST /users/123/activate
POST /orders/456/cancel
POST /payments/789/refund
 
# Search across resources
GET /search?q=laptop&type=products,reviews
 
# Aggregations
GET /stats/users/monthly
GET /reports/sales?year=2024

Part 6: Request and Response Bodies

Request Body Structure

For POST, PUT, and PATCH requests:

// Creating a user
POST /api/users
Content-Type: application/json
 
{
  "name": "John Doe",
  "email": "john@example.com",
  "password": "securePass123!",
  "profile": {
    "bio": "Software developer",
    "location": "San Francisco",
    "website": "https://johndoe.dev"
  },
  "preferences": {
    "newsletter": true,
    "theme": "dark"
  }
}

Response Body Structure

Single Resource

{
  "id": 123,
  "name": "John Doe",
  "email": "john@example.com",
  "profile": {
    "bio": "Software developer",
    "location": "San Francisco"
  },
  "createdAt": "2024-01-20T14:30:00Z",
  "updatedAt": "2024-01-20T14:30:00Z"
}

Collection with Metadata

{
  "data": [
    {"id": 1, "name": "John"},
    {"id": 2, "name": "Jane"},
    {"id": 3, "name": "Bob"}
  ],
  "meta": {
    "total": 150,
    "page": 1,
    "limit": 10,
    "totalPages": 15
  },
  "links": {
    "self": "/api/users?page=1&limit=10",
    "next": "/api/users?page=2&limit=10",
    "last": "/api/users?page=15&limit=10"
  }
}

Error Response Structure

Consistent error responses help clients handle failures:

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Request validation failed",
    "details": [
      {
        "field": "email",
        "message": "Must be a valid email address",
        "value": "not-an-email"
      },
      {
        "field": "password",
        "message": "Must be at least 8 characters",
        "value": "short"
      }
    ],
    "timestamp": "2024-01-20T15:00:00Z",
    "requestId": "req_abc123xyz"
  }
}

Date/Time Formats

Always use ISO 8601 format with timezone:

{
  "createdAt": "2024-01-20T14:30:00Z",        // UTC
  "scheduledFor": "2024-01-25T09:00:00-08:00", // With offset
  "date": "2024-01-20"                         // Date only
}

Part 7: Authentication and Authorization

Authentication Methods

1. API Keys

Simple but limited. Best for server-to-server communication.

# In header
GET /api/data HTTP/1.1
X-API-Key: sk_live_abc123xyz
 
# Or in query (less secure)
GET /api/data?api_key=sk_live_abc123xyz

Pros: Simple to implement Cons: No user context, easy to leak

2. JWT (JSON Web Tokens)

Stateless authentication. Most common for modern APIs.

GET /api/users/me HTTP/1.1
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4iLCJpYXQiOjE1MTYyMzkwMjJ9.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

JWT structure:

header.payload.signature
// Decoded payload
{
  "sub": "user_123",
  "name": "John Doe",
  "role": "admin",
  "iat": 1516239022,
  "exp": 1516325422
}

Pros: Stateless, contains user info, scalable Cons: Can't be revoked easily, size overhead

3. OAuth 2.0

For third-party access and social logins.

# Step 1: Redirect to authorization server
GET https://accounts.google.com/oauth/authorize?
  client_id=your-client-id&
  redirect_uri=https://yourapp.com/callback&
  scope=email profile&
  response_type=code
 
# Step 2: Exchange code for token
POST https://oauth2.googleapis.com/token
{
  "code": "auth_code_from_callback",
  "client_id": "your-client-id",
  "client_secret": "your-secret",
  "grant_type": "authorization_code"
}
 
# Step 3: Use access token
GET /api/users/me HTTP/1.1
Authorization: Bearer access_token_from_step_2

Pros: Industry standard, granular permissions, third-party support Cons: Complex flow, many moving parts

Authorization Patterns

Role-Based Access Control (RBAC)

// User with roles
{
  "id": 123,
  "email": "admin@example.com",
  "roles": ["admin", "editor"]
}
 
// Permission check
if (user.roles.includes("admin")) {
  // Allow action
}

Permission-Based Access Control

// User with specific permissions
{
  "id": 123,
  "permissions": [
    "users:read",
    "users:write",
    "posts:read",
    "posts:write",
    "posts:delete"
  ]
}

Security Headers

Always include these security headers:

# Response headers
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
Strict-Transport-Security: max-age=31536000; includeSubDomains
Content-Security-Policy: default-src 'self'

Part 8: Versioning Strategies

APIs evolve. Versioning lets you make breaking changes without breaking existing clients.

Version in the URL path:

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

Pros:

  • Obvious and explicit
  • Easy to route
  • Cacheable (different URLs)

Cons:

  • URL "pollution"
  • Clients must update URLs

Header Versioning

Custom header:

GET /api/users HTTP/1.1
Accept-Version: v2
 
# or
API-Version: 2024-01-20

Pros: Clean URLs Cons: Less visible, harder to test in browser

Query Parameter Versioning

GET /api/users?version=2

Pros: Easy to switch versions Cons: Optional parameter can be forgotten

Content Negotiation

Use Accept header:

GET /api/users HTTP/1.1
Accept: application/vnd.company.v2+json

Pros: Proper HTTP semantics Cons: Complex, hard to test

Versioning Best Practices

  1. Start with v1, even if you think you won't need versions
  2. Version the entire API, not individual endpoints
  3. Document deprecation timelines (e.g., "v1 supported until 2025-01-01")
  4. Use semantic versioning for major changes
  5. Provide migration guides when releasing new versions

Part 9: Pagination Patterns

Offset-Based Pagination

Traditional approach using page number and limit:

GET /api/users?page=2&limit=20
{
  "data": [...],
  "pagination": {
    "page": 2,
    "limit": 20,
    "total": 150,
    "totalPages": 8,
    "hasNext": true,
    "hasPrev": true
  }
}

Pros: Simple, can jump to any page Cons: Inconsistent with real-time data (items shift), slow on large offsets

Cursor-Based Pagination

Use an opaque cursor instead of page numbers:

GET /api/users?cursor=eyJpZCI6MTIzfQ&limit=20
{
  "data": [...],
  "cursors": {
    "before": "eyJpZCI6MTAzfQ",
    "after": "eyJpZCI6MTIzfQ",
    "hasMore": true
  }
}

Pros: Consistent results, handles real-time data, efficient Cons: Can't jump to specific page, cursor can become invalid

Keyset Pagination

Similar to cursor, but uses actual field values:

# First page
GET /api/users?limit=20
 
# Next page (after last item's created_at)
GET /api/users?created_after=2024-01-20T14:30:00Z&limit=20

Pros: Extremely efficient (uses indexes), stable Cons: Requires sortable unique field, can't skip pages

Choosing a Pagination Strategy

Use CaseRecommended
Admin dashboards with page numbersOffset
Social media feedsCursor
High-performance, large datasetsKeyset
Simple APIs with stable dataOffset
Real-time dataCursor or Keyset

Part 10: Filtering, Sorting, and Searching

Filtering

Simple equality filters:

GET /api/products?category=electronics&brand=apple

Range filters:

GET /api/products?price_min=100&price_max=500
GET /api/orders?created_after=2024-01-01&created_before=2024-02-01

Complex filters (JSON):

GET /api/products?filter={"category":"electronics","price":{"$gte":100,"$lte":500}}

Sorting

Single field:

GET /api/products?sort=price          # Ascending
GET /api/products?sort=-price         # Descending (prefix with -)

Multiple fields:

GET /api/products?sort=-featured,price   # Featured first, then by price

Simple search:

GET /api/products?q=wireless+headphones

Advanced search:

GET /api/search?q=laptop&fields=name,description&fuzzy=true

Combining Everything

GET /api/products?
  category=electronics&
  price_min=100&
  price_max=1000&
  sort=-rating,price&
  q=laptop&
  page=1&
  limit=20&
  fields=id,name,price,rating

Part 11: HATEOAS (Hypermedia)

HATEOAS = Hypermedia as the Engine of Application State

The idea: responses include links to related resources and available actions.

Example Without HATEOAS

{
  "id": 123,
  "status": "pending",
  "total": 99.99
}

Client must know:

  • How to get order details
  • What actions are available
  • Where to find related resources

Example With HATEOAS

{
  "id": 123,
  "status": "pending",
  "total": 99.99,
  "_links": {
    "self": {"href": "/api/orders/123"},
    "items": {"href": "/api/orders/123/items"},
    "customer": {"href": "/api/customers/456"},
    "cancel": {
      "href": "/api/orders/123/cancel",
      "method": "POST"
    },
    "payment": {
      "href": "/api/orders/123/payment",
      "method": "POST"
    }
  }
}

Benefits of HATEOAS

  1. Self-documenting: Response tells you what's possible
  2. Loose coupling: Client doesn't hardcode URLs
  3. Evolvability: Server can change URLs without breaking clients

Common HATEOAS Formats

HAL (Hypertext Application Language)

{
  "id": 123,
  "_links": {
    "self": {"href": "/orders/123"},
    "items": {"href": "/orders/123/items"}
  },
  "_embedded": {
    "items": [
      {"id": 1, "name": "Product A"}
    ]
  }
}

JSON:API

{
  "data": {
    "type": "orders",
    "id": "123",
    "attributes": {"status": "pending"},
    "relationships": {
      "items": {
        "links": {
          "related": "/orders/123/items"
        }
      }
    }
  }
}

Part 12: REST API Best Practices

1. Use Proper HTTP Methods

✅ GET for reading
✅ POST for creating
✅ PUT/PATCH for updating
✅ DELETE for deleting
 
❌ POST /deleteUser
❌ GET /createUser?name=John

2. Return Appropriate Status Codes

✅ 200 for successful GET/PUT/PATCH
✅ 201 for successful POST (creation)
✅ 204 for successful DELETE
✅ 400 for bad client input
✅ 401 for authentication failure
✅ 403 for authorization failure
✅ 404 for missing resources
✅ 500 for server errors
 
❌ 200 for everything with "error" in body

3. Use Consistent Naming

✅ /users (plural, lowercase)
✅ /user-profiles (hyphenated)
✅ /orders/123/items (nested resources)
 
❌ /User
❌ /user_profile
❌ /getOrders

4. Version Your API

✅ /api/v1/users
✅ /api/v2/users
 
❌ /api/users (no version = trouble later)

5. Implement Proper Error Handling

// ✅ Good: Structured error with details
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Validation failed",
    "details": [
      {"field": "email", "message": "Invalid email format"}
    ]
  }
}
 
// ❌ Bad: Generic error message
{
  "error": "Something went wrong"
}

6. Use HTTPS Always

✅ https://api.example.com
❌ http://api.example.com

7. Implement Rate Limiting

HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1640995200
Retry-After: 60
 
{
  "error": "Rate limit exceeded"
}

8. Document Your API

Use OpenAPI/Swagger for documentation:

openapi: 3.0.0
info:
  title: My API
  version: 1.0.0
paths:
  /users:
    get:
      summary: List all users
      responses:
        '200':
          description: Successful response

9. Support Content Negotiation

# Request
Accept: application/json
 
# Response
Content-Type: application/json

10. Include Request IDs

For debugging and tracing:

HTTP/1.1 500 Internal Server Error
X-Request-ID: req_abc123xyz
 
{
  "error": "Internal server error",
  "requestId": "req_abc123xyz"
}

Part 13: REST vs Alternatives

REST vs GraphQL

AspectRESTGraphQL
Data fetchingFixed endpointsFlexible queries
Over-fetchingCommonSolved
Under-fetchingRequires multiple callsSingle query
CachingHTTP cachingCustom caching
Learning curveLowerHigher
ToolingMatureGrowing
Real-timeWebSocket/SSESubscriptions

Use REST when: CRUD operations, simple data, caching important Use GraphQL when: Complex data relationships, mobile apps, flexible clients

REST vs gRPC

AspectRESTgRPC
ProtocolHTTP/1.1, HTTP/2HTTP/2
FormatJSON (text)Protocol Buffers (binary)
PerformanceGoodExcellent
StreamingLimitedBuilt-in
Browser supportNativeRequires proxy
ContractOpenAPI (optional)Required (.proto)

Use REST when: Public APIs, browser clients, simplicity Use gRPC when: Microservices, high-performance, bi-directional streaming

When to Use REST

REST is ideal for:

  • ✅ Public APIs consumed by third parties
  • ✅ Web applications with browser clients
  • ✅ CRUD-heavy applications
  • ✅ When caching is important
  • ✅ Simple, well-understood requirements
  • ✅ Teams new to API development

Summary

REST APIs are the backbone of modern web development. Here's what we covered:

Key Concepts

  1. Resources are the core abstraction—everything is a resource
  2. HTTP methods map to CRUD operations (GET, POST, PUT, PATCH, DELETE)
  3. Status codes communicate what happened (2xx success, 4xx client error, 5xx server error)
  4. URLs should use nouns, be plural, and show relationships

Best Practices Checklist

✅ Use nouns for resources, not verbs
✅ Use proper HTTP methods and status codes
✅ Version your API from day one
✅ Implement authentication (JWT, OAuth)
✅ Use HTTPS everywhere
✅ Paginate large collections
✅ Document with OpenAPI/Swagger
✅ Return consistent error responses
✅ Implement rate limiting
✅ Include request IDs for debugging

What's Next?

Now that you understand REST API fundamentals, explore:

Build REST APIs:

Document Your APIs:

Deep Dive:


Practice Exercises

Exercise 1: Design a Blog API

Design endpoints for a blog with posts, comments, and users:

GET /api/posts
POST /api/posts
GET /api/posts/:id
PUT /api/posts/:id
DELETE /api/posts/:id
GET /api/posts/:id/comments
POST /api/posts/:id/comments
GET /api/users/:id/posts

Exercise 2: Status Code Quiz

What status code would you return for:

  1. User not authenticated → ?
  2. User authenticated but not allowed → ?
  3. Email already exists → ?
  4. Invalid JSON in request → ?
  5. Resource created successfully → ?
Answers
  1. 401 Unauthorized
  2. 403 Forbidden
  3. 409 Conflict
  4. 400 Bad Request
  5. 201 Created

Exercise 3: Implement a REST API

Build a simple REST API with these endpoints:

GET /api/todos         - List all todos
POST /api/todos        - Create a todo
GET /api/todos/:id     - Get a todo
PATCH /api/todos/:id   - Update a todo
DELETE /api/todos/:id  - Delete a todo

Use your favorite framework (Express.js, FastAPI, Spring Boot) and implement proper:

  • Status codes
  • Error handling
  • Request validation

REST APIs may seem simple on the surface, but mastering them requires understanding HTTP, resource design, and many subtle best practices. With this foundation, you're ready to build robust, scalable APIs that developers love to use.

Happy API building! 🚀

📬 Subscribe to Newsletter

Get the latest blog posts delivered to your inbox every week. No spam, unsubscribe anytime.

We respect your privacy. Unsubscribe at any time.

💬 Comments

Sign in to leave a comment

We'll never post without your permission.