REST API (Representational State Transfer)

The most common way to build web APIs - use HTTP verbs on URLs to manage resources.

5 min read

What is REST?

REST is a set of guidelines for building APIs. It's not a protocol or standard—it's an architectural style. An API is "RESTful" when it follows these conventions.

The core idea: treat everything as a resource with a URL, and use standard HTTP methods to interact with it.

GET    /users        → List all users
GET    /users/123    → Get user 123
POST   /users        → Create a new user
PUT    /users/123    → Replace user 123
PATCH  /users/123    → Update part of user 123
DELETE /users/123    → Delete user 123

That's it. If you understand this table, you understand 90% of REST.

HTTP Methods

MethodPurposeHas BodyIdempotentSafe
GETRead dataNoYesYes
POSTCreate new resourceYesNoNo
PUTReplace entire resourceYesYesNo
PATCHPartial updateYesYes*No
DELETERemove resourceNoYesNo
HEADGet headers onlyNoYesYes
OPTIONSGet allowed methodsNoYesYes

Idempotent = calling it multiple times has the same effect as calling once. Safe = doesn't modify server state.

REST Principles

1. Stateless

Every request contains all the information needed. The server doesn't remember previous requests.

javascript
// Bad: server remembers who you are
fetch("/api/my-orders");

// Good: every request identifies itself
fetch("/api/orders", {
  headers: { "Authorization": "Bearer eyJhbG..." }
});

2. Resource-Based URLs

URLs represent things (nouns), not actions (verbs).

// Good - resources
GET /articles
GET /articles/42
GET /articles/42/comments

// Bad - actions in URL
GET /getArticle?id=42
POST /createArticle
GET /fetchAllArticles

3. Use HTTP Methods Correctly

javascript
// Create a new post
const response = await fetch("/api/posts", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    title: "REST APIs Explained",
    content: "REST is an architectural style..."
  })
});

// Update an existing post
await fetch("/api/posts/42", {
  method: "PUT",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    title: "REST APIs Explained (Updated)",
    content: "REST is an architectural style..."
  })
});

// Delete a post
await fetch("/api/posts/42", {
  method: "DELETE"
});

4. Proper Status Codes

Use HTTP status codes to indicate what happened:

CodeMeaningWhen to Use
200OKSuccessful GET, PUT, PATCH
201CreatedSuccessful POST that created something
204No ContentSuccessful DELETE
400Bad RequestInvalid input from client
401UnauthorizedMissing or invalid auth
403ForbiddenValid auth but not allowed
404Not FoundResource doesn't exist
422UnprocessableValidation failed
429Too Many RequestsRate limited
500Server ErrorSomething broke on the server

REST vs Other Styles

StyleBest ForDownside
RESTCRUD operations, simple APIsOver-fetching, multiple roundtrips
GraphQLComplex queries, mobile appsLearning curve, caching complexity
gRPCMicroservices, performance-criticalBrowser support, debugging
WebSocketReal-time, bidirectionalConnection management

Example: Complete CRUD Flow

javascript
const API_BASE = "https://api.example.com";

// CREATE - Post new user
async function createUser(user) {
  const res = await fetch(`${API_BASE}/users`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(user)
  });
  if (res.status !== 201) throw new Error("Failed to create");
  return res.json();
}

// READ - Get user by ID
async function getUser(id) {
  const res = await fetch(`${API_BASE}/users/${id}`);
  if (res.status === 404) return null;
  if (!res.ok) throw new Error("Failed to fetch");
  return res.json();
}

// UPDATE - Partial update
async function updateUser(id, changes) {
  const res = await fetch(`${API_BASE}/users/${id}`, {
    method: "PATCH",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(changes)
  });
  if (!res.ok) throw new Error("Failed to update");
  return res.json();
}

// DELETE - Remove user
async function deleteUser(id) {
  const res = await fetch(`${API_BASE}/users/${id}`, {
    method: "DELETE"
  });
  if (res.status !== 204) throw new Error("Failed to delete");
}

Common Gotchas

⚠️PUT vs PATCH

PUT replaces the entire resource. If you PUT { "name": "New Name" } to a user, you'll lose all other fields. Use PATCH for partial updates.

ℹ️Nesting Depth

Keep URL nesting shallow. /users/123/posts/456/comments/789/likes is a sign you need to rethink your API design. Two levels deep is usually the max.

  • POST isn't idempotent - Clicking "Submit" twice creates two resources. Disable the button or use idempotency keys.
  • GET requests shouldn't modify data - Never use GET to delete or update. Prefetching and caching will cause chaos.
  • Versioning matters - Start with /v1/ from day one. You'll thank yourself later.
  • Plural nouns - Use /users, not /user. Be consistent across your entire API.
  • Don't return 200 for errors - If something failed, use an error status code. { "status": 200, "error": "Not found" } breaks every HTTP client.

Try It

Format API Responses

"REST: because someone had to standardize the 47 different ways developers were building APIs in 2000."