# REST API Design Made Simple with Express.js


 REST API is a standard way for the client to communicate with the server. Instead of mysterious endpoints like `/getUser` or `/fetchUserData`, REST uses predictable HTTP methods and resource-based URLs. Once you understand REST, building APIs becomes straightforward.

This is about understanding REST principles and building proper APIs with Express.

* * *

## What REST API Means

REST stands for Representational State Transfer. It's a set of rules for building APIs.

### Simple Definition

REST is a standard way to structure requests and responses between client and server using HTTP methods and resource URLs.

### The Communication Problem

Without REST, APIs are chaotic:

```
GET /getUser?id=1
POST /fetchUserData
PUT /updateUserInfo
DELETE /removeUser?userId=1
GET /getAllUsers
```

Different names for similar operations. Confusing.

With REST, APIs are predictable:

```
GET /users/1        (get user)
POST /users         (create user)
PUT /users/1        (update user)
DELETE /users/1     (delete user)
GET /users          (get all users)
```

Same resource, different methods. Crystal clear.

### Real Analogy

Think of a restaurant's menu (API):

Without REST:

```
bringSoup
bringBread
removePlate
giveBill
bringsalad
```

Confusing. No pattern.

With REST:

```
Action: BRING, Resource: SOUP
Action: BRING, Resource: BREAD
Action: REMOVE, Resource: PLATE
Action: GIVE, Resource: BILL
```

Clear pattern. Easy to understand.

### Why REST Matters

REST makes APIs:

- Predictable (you can guess the URL)
- Consistent (same patterns everywhere)
- Easy to document
- Easy for clients to use
- Standard across the web

* * *

## Resources in REST Architecture

Resources are the things your API manages.

### What is a Resource?

A resource is anything your API handles: users, posts, comments, products, orders.

### Resource URLs

Resources are accessed via URLs:

```plaintext
/users          (users resource)
/posts          (posts resource)
/comments       (comments resource)
/products       (products resource)
```

### Specific Resources

Access specific resources with IDs:

```plaintext
/users/1        (user with ID 1)
/posts/42       (post with ID 42)
/comments/100   (comment with ID 100)
```

### Nested Resources

Related resources can be nested:

```plaintext
/users/1/posts           (posts by user 1)
/posts/42/comments       (comments on post 42)
/users/1/posts/5/comments (comments on post 5 by user 1)
```

### Resource Collections vs Specific Resources

```plaintext
/users          (collection: all users)
/users/1        (specific: user with ID 1)

/posts          (collection: all posts)
/posts/42       (specific: post with ID 42)
```

### Naming Conventions

Use plural nouns for resources:

```plaintext
✓ /users        (correct)
✗ /user         (incorrect)

✓ /posts        (correct)
✗ /post         (incorrect)

✓ /comments     (correct)
✗ /comment      (incorrect)
```

Use lowercase and hyphens for multi-word resources:

```plaintext
✓ /user-profiles
✓ /blog-posts
✓ /shopping-carts

✗ /userProfiles (camelCase)
✗ /BlogPosts    (PascalCase)
```

* * *

## HTTP Methods: GET, POST, PUT, DELETE

HTTP methods tell the server what action to perform on a resource.

### GET: Retrieve Data

GET retrieves existing data. Safe and doesn't change anything.

```plaintext
GET /users           → Get all users
GET /users/1         → Get user with ID 1
GET /users/1/posts   → Get posts by user 1
```

### Code Example: GET

```javascript
const express = require("express");
const app = express();

// Get all users
app.get("/users", (req, res) => {
  const users = [
    { id: 1, name: "Alice" },
    { id: 2, name: "Bob" }
  ];
  res.json(users);
});

// Get specific user
app.get("/users/:id", (req, res) => {
  const userId = req.params.id;
  const user = { id: userId, name: "Alice" };
  res.json(user);
});
```

### POST: Create Data

POST creates new resources.

```plaintext
POST /users              → Create new user
POST /posts              → Create new post
POST /users/1/posts      → Create post for user 1
```

The request body contains the data to create.

### Code Example: POST

```javascript
app.use(express.json());

// Create new user
app.post("/users", (req, res) => {
  const newUser = {
    id: 3,
    name: req.body.name,
    email: req.body.email
  };
  
  res.status(201).json(newUser);
});
```

Test with curl:

```bash
curl -X POST http://localhost:3000/users \
  -H "Content-Type: application/json" \
  -d '{"name":"Charlie","email":"charlie@example.com"}'
```

### PUT: Update Data

PUT updates entire resources.

```plaintext
PUT /users/1        → Update user 1
PUT /posts/42       → Update post 42
```

The request body contains the updated data.

### Code Example: PUT

```javascript
// Update entire user
app.put("/users/:id", (req, res) => {
  const userId = req.params.id;
  const updatedUser = {
    id: userId,
    name: req.body.name,
    email: req.body.email,
    age: req.body.age
  };
  
  res.json(updatedUser);
});
```

Test with curl:

```bash
curl -X PUT http://localhost:3000/users/1 \
  -H "Content-Type: application/json" \
  -d '{"name":"Alice Updated","email":"alice.new@example.com","age":26}'
```

### PATCH: Partial Update

PATCH updates specific fields (not full replacement).

```plaintext
PATCH /users/1      → Update some fields of user 1
```

### Code Example: PATCH

```javascript
// Update specific fields
app.patch("/users/:id", (req, res) => {
  const userId = req.params.id;
  
  // Only update provided fields
  const updates = {
    id: userId,
    name: req.body.name || "Alice",  // Keep existing if not provided
    email: req.body.email || "alice@example.com"
  };
  
  res.json(updates);
});
```

### DELETE: Remove Data

DELETE removes resources.

```plaintext
DELETE /users/1     → Delete user 1
DELETE /posts/42    → Delete post 42
```

### Code Example: DELETE

```javascript
// Delete user
app.delete("/users/:id", (req, res) => {
  const userId = req.params.id;
  
  res.json({ message: `User ${userId} deleted` });
});
```

Test with curl:

```bash
curl -X DELETE http://localhost:3000/users/1
```

### CRUD vs HTTP Methods

```plaintext
CRUD Operation    HTTP Method    Example
─────────────────────────────────────────────
Create            POST           POST /users
Read              GET            GET /users/1
Update            PUT/PATCH      PUT /users/1
Delete            DELETE         DELETE /users/1
```

* * *

## Status Codes Basics

Status codes tell the client if the request succeeded or failed.

### 2xx: Success

Request was successful.

```plaintext
200 OK              - Request succeeded
201 Created         - Resource created
204 No Content      - Success, no data to return
```

### 4xx: Client Error

Something wrong with the request.

```plaintext
400 Bad Request     - Invalid request data
401 Unauthorized    - Not authenticated
403 Forbidden       - Authenticated but not allowed
404 Not Found       - Resource doesn't exist
```

### 5xx: Server Error

Something wrong on the server.

```plaintext
500 Internal Server Error - Server error
503 Service Unavailable   - Server down
```

### Using Status Codes

```javascript
app.post("/users", (req, res) => {
  if (!req.body.email) {
    return res.status(400).json({ error: "Email required" });
  }
  
  const newUser = { id: 1, name: req.body.name };
  res.status(201).json(newUser);  // 201 Created
});

app.get("/users/:id", (req, res) => {
  const user = findUser(req.params.id);
  
  if (!user) {
    return res.status(404).json({ error: "User not found" });
  }
  
  res.status(200).json(user);  // 200 OK
});
```

### Common Status Codes

```plaintext
GET request         → 200 OK
POST success        → 201 Created
No content to show  → 204 No Content
Invalid request     → 400 Bad Request
Not authenticated   → 401 Unauthorized
Resource not found  → 404 Not Found
Server error        → 500 Internal Server Error
```

* * *

## Designing Routes Using REST Principles

### Users Resource Example

```plaintext
GET /users                  → Get all users
GET /users/1                → Get user 1
POST /users                 → Create new user
PUT /users/1                → Update user 1
PATCH /users/1              → Partially update user 1
DELETE /users/1             → Delete user 1
```

### Posts Resource Example

```plaintext
GET /posts                  → Get all posts
GET /posts/42               → Get post 42
POST /posts                 → Create new post
PUT /posts/42               → Update post 42
DELETE /posts/42            → Delete post 42
```

### Nested Resources

User's posts:

```plaintext
GET /users/1/posts          → Get posts by user 1
POST /users/1/posts         → Create post for user 1
GET /users/1/posts/5        → Get post 5 by user 1
DELETE /users/1/posts/5     → Delete post 5 by user 1
```

### Complete API Structure

```plaintext
Users
├─ GET /users               (list all)
├─ POST /users              (create)
├─ GET /users/1             (get one)
├─ PUT /users/1             (update)
└─ DELETE /users/1          (delete)

Posts
├─ GET /posts               (list all)
├─ POST /posts              (create)
├─ GET /posts/1             (get one)
├─ PUT /posts/1             (update)
└─ DELETE /posts/1          (delete)

User's Posts
├─ GET /users/1/posts       (user's posts)
└─ POST /users/1/posts      (user creates post)
```

* * *

## REST Request-Response Lifecycle

```plaintext
Client sends request
├─ Method: POST
├─ URL: /users
└─ Body: { name: "Alice", email: "alice@example.com" }
      |
      v
Server receives request
      |
      v
Server validates data
├─ Valid? → Continue
└─ Invalid? → Return 400 error
      |
      v
Server creates resource
├─ Success? → Continue
└─ Error? → Return 500 error
      |
      v
Server sends response
├─ Status: 201 Created
└─ Body: { id: 1, name: "Alice", email: "alice@example.com" }
      |
      v
Client receives response
      |
      v
Client displays data or error
```

* * *

## Example Resource: Users

Building a complete users API.

### Simple Users API

```javascript
const express = require("express");
const app = express();
app.use(express.json());

// In-memory database
let users = [
  { id: 1, name: "Alice", email: "alice@example.com" },
  { id: 2, name: "Bob", email: "bob@example.com" }
];

let nextId = 3;

// GET all users
app.get("/users", (req, res) => {
  res.json(users);
});

// GET specific user
app.get("/users/:id", (req, res) => {
  const user = users.find(u => u.id === parseInt(req.params.id));
  
  if (!user) {
    return res.status(404).json({ error: "User not found" });
  }
  
  res.json(user);
});

// POST create user
app.post("/users", (req, res) => {
  // Validate
  if (!req.body.name || !req.body.email) {
    return res.status(400).json({ error: "Name and email required" });
  }
  
  // Create
  const newUser = {
    id: nextId++,
    name: req.body.name,
    email: req.body.email
  };
  
  users.push(newUser);
  
  // Return 201 Created
  res.status(201).json(newUser);
});

// PUT update user
app.put("/users/:id", (req, res) => {
  const user = users.find(u => u.id === parseInt(req.params.id));
  
  if (!user) {
    return res.status(404).json({ error: "User not found" });
  }
  
  // Validate
  if (!req.body.name || !req.body.email) {
    return res.status(400).json({ error: "Name and email required" });
  }
  
  // Update
  user.name = req.body.name;
  user.email = req.body.email;
  
  res.json(user);
});

// PATCH partial update
app.patch("/users/:id", (req, res) => {
  const user = users.find(u => u.id === parseInt(req.params.id));
  
  if (!user) {
    return res.status(404).json({ error: "User not found" });
  }
  
  // Update only provided fields
  if (req.body.name) user.name = req.body.name;
  if (req.body.email) user.email = req.body.email;
  
  res.json(user);
});

// DELETE user
app.delete("/users/:id", (req, res) => {
  const index = users.findIndex(u => u.id === parseInt(req.params.id));
  
  if (index === -1) {
    return res.status(404).json({ error: "User not found" });
  }
  
  const deletedUser = users.splice(index, 1);
  res.json({ message: "User deleted", user: deletedUser[0] });
});

app.listen(3000, () => {
  console.log("API running on http://localhost:3000");
});
```

### Testing the API

Get all users:

```bash
curl http://localhost:3000/users
# [{"id":1,"name":"Alice","email":"alice@example.com"},{"id":2,"name":"Bob","email":"bob@example.com"}]
```

Get one user:

```bash
curl http://localhost:3000/users/1
# {"id":1,"name":"Alice","email":"alice@example.com"}
```

Create user:

```bash
curl -X POST http://localhost:3000/users \
  -H "Content-Type: application/json" \
  -d '{"name":"Charlie","email":"charlie@example.com"}'
# {"id":3,"name":"Charlie","email":"charlie@example.com"}
```

Update user:

```bash
curl -X PUT http://localhost:3000/users/1 \
  -H "Content-Type: application/json" \
  -d '{"name":"Alice Updated","email":"alice.new@example.com"}'
# {"id":1,"name":"Alice Updated","email":"alice.new@example.com"}
```

Partial update:

```bash
curl -X PATCH http://localhost:3000/users/1 \
  -H "Content-Type: application/json" \
  -d '{"email":"newemail@example.com"}'
# {"id":1,"name":"Alice Updated","email":"newemail@example.com"}
```

Delete user:

```bash
curl -X DELETE http://localhost:3000/users/1
# {"message":"User deleted","user":{"id":1,"name":"Alice Updated","email":"newemail@example.com"}}
```

* * *

## API Request-Response Examples

### GET Request

```plaintext
Request:
────────
GET /users/1 HTTP/1.1
Host: localhost:3000

Response:
─────────
HTTP/1.1 200 OK
Content-Type: application/json

{
  "id": 1,
  "name": "Alice",
  "email": "alice@example.com"
}
```

### POST Request

```plaintext
Request:
────────
POST /users HTTP/1.1
Host: localhost:3000
Content-Type: application/json

{
  "name": "Charlie",
  "email": "charlie@example.com"
}

Response:
─────────
HTTP/1.1 201 Created
Content-Type: application/json

{
  "id": 3,
  "name": "Charlie",
  "email": "charlie@example.com"
}
```

### Error Response

```plaintext
Request:
────────
GET /users/999 HTTP/1.1
Host: localhost:3000

Response:
─────────
HTTP/1.1 404 Not Found
Content-Type: application/json

{
  "error": "User not found"
}
```

* * *

## Best Practices for REST APIs

### Use Correct HTTP Methods

```javascript
// ✓ Correct
GET /users          (retrieve)
POST /users         (create)
PUT /users/1        (update)
DELETE /users/1     (delete)

// ✗ Wrong
GET /deleteUser?id=1     (wrong method)
POST /getUsers           (should be GET)
GET /updateUser?id=1     (wrong method)
```

### Use Correct Status Codes

```javascript
// ✓ Correct
app.post("/users", (req, res) => {
  res.status(201).json(newUser);  // 201 for create
});

app.get("/users/:id", (req, res) => {
  if (!user) {
    return res.status(404).json({ error: "Not found" });  // 404 for missing
  }
  res.json(user);  // 200 for success
});

// ✗ Wrong
app.post("/users", (req, res) => {
  res.json(newUser);  // No status (defaults to 200, should be 201)
});
```

### Use Plural Nouns for Resources

```javascript
// ✓ Correct
GET /users
POST /posts
DELETE /comments/1

// ✗ Wrong
GET /user
POST /post
DELETE /comment/1
```

### Use Logical Nesting

```javascript
// ✓ Correct (related resources)
GET /users/1/posts      (user's posts)

// ✗ Wrong (too deeply nested)
GET /users/1/posts/2/comments/3/author/replies
```

### Validate Input

```javascript
app.post("/users", (req, res) => {
  // ✓ Validate
  if (!req.body.name || !req.body.email) {
    return res.status(400).json({ error: "Invalid data" });
  }
  
  // Process...
});
```

* * *

## Practice Assignment

**1. Plan a products API:**

```javascript
// Design routes for products resource
// GET /products
// GET /products/:id
// POST /products
// PUT /products/:id
// DELETE /products/:id
// Write route signatures
```

**2. Build simple users API:**

```javascript
// Create Express server
// Implement all CRUD operations for users
// Test each endpoint with curl
// Return correct status codes
```

**3. Add validation:**

```javascript
// Add input validation to POST /users
// Check required fields
// Return 400 for invalid data
// Test with valid and invalid requests
```

**4. Nested resources:**

```javascript
// Create users and posts resources
// Implement GET /users/:id/posts
// Get all posts for a specific user
// Test the nested route
```

**5. Error handling:**

```javascript
// Add proper error handling
// Return 404 for missing resources
// Return 400 for bad requests
// Return 500 for server errors
// Test error scenarios
```

* * *

## Quick Recap

- **REST** is a standard for building predictable, consistent APIs.

- **Resources** are things your API manages: users, posts, products.

- **HTTP methods** tell the server what action to perform: GET, POST, PUT, DELETE.

- **GET** retrieves data without changing anything.

- **POST** creates new resources.

- **PUT** updates entire resources.

- **PATCH** updates specific fields.

- **DELETE** removes resources.

- **Status codes** tell the client if the request succeeded or failed.

- **2xx codes** indicate success (200 OK, 201 Created).

- **4xx codes** indicate client errors (400 Bad Request, 404 Not Found).

- **5xx codes** indicate server errors (500 Internal Server Error).

- Use **plural nouns** for resource names: `/users`, not `/user`.

- Use **lowercase and hyphens** for multi-word resources: `/user-profiles`.

- **Collection endpoints** return all resources: `GET /users`.

- **Specific endpoints** use IDs: `GET /users/1`.

- **Nested resources** show relationships: `GET /users/1/posts`.

- **Request validation** prevents bad data from being saved.

- **Proper status codes** help clients understand what happened.

- **Consistent naming** makes APIs predictable and easy to use.

- **REST design** makes APIs intuitive even for new users.

REST APIs are the standard for building modern web services.

* * *

> If you enjoyed this article, check out my other blogs on this profile. Connect with me: [LinkedIn](https://linkedin.com/in/rajharsh03) | [GitHub](https://github.com/RajHarsh03) | [X (Twitter)](https://x.com/rajharsh03)
