REST API Basics

May 18, 2026
#web-development #rest #api #http

APIs (Application Programming Interfaces) are how software systems talk to each other. When you check the weather on your phone, scroll through social media, or make an online payment, your app is communicating with a server through an API. REST is the most common architectural style for web APIs, and understanding it is essential for any developer.

This tutorial covers what REST is, how HTTP methods map to operations, and how to work with REST APIs using JavaScript.

What Is REST?

REST (Representational State Transfer) is a set of conventions for building web APIs. A REST API exposes resources (data objects like users, posts, or products) at specific URLs, and you interact with them using standard HTTP methods.

Key principles:

  • Resources have URLs — Each thing you can interact with has a unique address
  • Standard HTTP methods — GET to read, POST to create, PUT/PATCH to update, DELETE to remove
  • Stateless — Each request contains all the information needed; the server doesn’t remember previous requests
  • JSON responses — Most modern APIs return data as JSON

HTTP Methods

Method Purpose Example
GET Read/retrieve data Get a list of users
POST Create new data Create a new user
PUT Replace existing data entirely Replace a user’s profile
PATCH Partially update data Update just a user’s email
DELETE Remove data Delete a user

URL Structure

REST APIs follow predictable URL patterns:

GET    /api/users          → List all users
GET    /api/users/42       → Get user with ID 42
POST   /api/users          → Create a new user
PUT    /api/users/42       → Replace user 42
PATCH  /api/users/42       → Update user 42 partially
DELETE /api/users/42       → Delete user 42

Nested resources:

GET    /api/users/42/posts      → Get all posts by user 42
POST   /api/users/42/posts      → Create a post for user 42
GET    /api/users/42/posts/7    → Get post 7 by user 42

Making API Requests with fetch

JavaScript’s fetch API is the standard way to make HTTP requests in the browser:

GET — Reading data

async function getUsers() {
    const response = await fetch("https://jsonplaceholder.typicode.com/users");

    if (!response.ok) {
        throw new Error(`HTTP ${response.status}`);
    }

    const users = await response.json();
    console.log(users);
    return users;
}

POST — Creating data

async function createUser(userData) {
    const response = await fetch("https://jsonplaceholder.typicode.com/users", {
        method: "POST",
        headers: {
            "Content-Type": "application/json"
        },
        body: JSON.stringify(userData)
    });

    if (!response.ok) {
        throw new Error(`HTTP ${response.status}`);
    }

    const newUser = await response.json();
    console.log("Created:", newUser);
    return newUser;
}

createUser({ name: "Alice", email: "alice@example.com" });

PUT — Replacing data

async function replaceUser(id, userData) {
    const response = await fetch(`https://jsonplaceholder.typicode.com/users/${id}`, {
        method: "PUT",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(userData)
    });

    return response.json();
}

PATCH — Partial update

async function updateEmail(id, newEmail) {
    const response = await fetch(`https://jsonplaceholder.typicode.com/users/${id}`, {
        method: "PATCH",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ email: newEmail })
    });

    return response.json();
}

DELETE — Removing data

async function deleteUser(id) {
    const response = await fetch(`https://jsonplaceholder.typicode.com/users/${id}`, {
        method: "DELETE"
    });

    if (!response.ok) {
        throw new Error(`Failed to delete user ${id}`);
    }

    console.log(`User ${id} deleted`);
}

HTTP Status Codes

The server communicates success or failure through status codes:

Code Meaning When You’ll See It
200 OK Successful GET, PUT, PATCH
201 Created Successful POST
204 No Content Successful DELETE (no body returned)
400 Bad Request Invalid data sent
401 Unauthorized Missing or invalid authentication
403 Forbidden Authenticated but not allowed
404 Not Found Resource doesn’t exist
422 Unprocessable Entity Validation errors
429 Too Many Requests Rate limited
500 Internal Server Error Server-side bug

Always check response.ok (true for 200–299 status codes) before processing the response body.

Query Parameters

Use query parameters for filtering, sorting, and pagination:

GET /api/users?role=admin              → Filter by role
GET /api/users?sort=name&order=asc     → Sort by name ascending
GET /api/users?page=2&limit=20         → Page 2, 20 items per page
GET /api/posts?userId=42&status=published  → Multiple filters

In JavaScript:

async function searchUsers(filters) {
    const params = new URLSearchParams(filters);
    const response = await fetch(`/api/users?${params}`);
    return response.json();
}

searchUsers({ role: "admin", sort: "name", limit: "10" });
// Fetches: /api/users?role=admin&sort=name&limit=10

Request and Response Headers

Common request headers

const response = await fetch("/api/data", {
    headers: {
        "Content-Type": "application/json",    // what you're sending
        "Accept": "application/json",          // what you want back
        "Authorization": "Bearer eyJhbG..."    // authentication token
    }
});

Reading response headers

const response = await fetch("/api/users");

console.log(response.headers.get("Content-Type"));
console.log(response.headers.get("X-Total-Count"));  // custom header for total items

Authentication

Most APIs require authentication. Common approaches:

API Key (simple, less secure)

// As a query parameter
fetch("/api/data?api_key=your-key-here");

// As a header (preferred)
fetch("/api/data", {
    headers: { "X-API-Key": "your-key-here" }
});

Bearer Token (JWT)

const token = "eyJhbGciOiJIUzI1NiIs...";

fetch("/api/protected-resource", {
    headers: { "Authorization": `Bearer ${token}` }
});

Error Handling

A robust API client handles errors gracefully:

async function apiRequest(url, options = {}) {
    try {
        const response = await fetch(url, {
            headers: { "Content-Type": "application/json" },
            ...options
        });

        if (!response.ok) {
            const errorBody = await response.json().catch(() => ({}));
            throw new Error(errorBody.message || `HTTP ${response.status}`);
        }

        // Handle 204 No Content
        if (response.status === 204) return null;

        return await response.json();
    } catch (error) {
        if (error.name === "TypeError") {
            // Network error (no internet, DNS failure, etc.)
            throw new Error("Network error — check your connection");
        }
        throw error;
    }
}

Pagination

APIs return large datasets in pages. Common patterns:

Offset-based

async function getAllUsers() {
    const allUsers = [];
    let page = 1;
    const limit = 50;

    while (true) {
        const users = await apiRequest(`/api/users?page=${page}&limit=${limit}`);
        allUsers.push(...users);

        if (users.length < limit) break;  // last page
        page++;
    }

    return allUsers;
}

Cursor-based

async function getAllPosts() {
    const allPosts = [];
    let cursor = null;

    while (true) {
        const url = cursor ? `/api/posts?cursor=${cursor}` : "/api/posts";
        const { data, nextCursor } = await apiRequest(url);
        allPosts.push(...data);

        if (!nextCursor) break;
        cursor = nextCursor;
    }

    return allPosts;
}

Testing APIs

Before writing code, test APIs with tools:

  • Browser DevTools (Network tab) — see requests your page makes
  • curl (command line) — quick tests from the terminal
  • Postman / Insomnia — GUI tools for building and saving requests
# GET request
curl https://jsonplaceholder.typicode.com/users/1

# POST request
curl -X POST https://jsonplaceholder.typicode.com/users \
  -H "Content-Type: application/json" \
  -d '{"name": "Alice", "email": "alice@example.com"}'

REST Best Practices

When consuming APIs:

  • Always check status codes before processing responses
  • Handle network errors (offline, timeouts)
  • Respect rate limits (check for 429 responses and Retry-After headers)
  • Don’t hardcode URLs — use a base URL variable

When designing APIs:

  • Use nouns for URLs (/users), not verbs (/getUsers)
  • Use plural nouns (/users, not /user)
  • Return appropriate status codes
  • Include pagination for list endpoints
  • Version your API (/api/v1/users)

What’s Next

Now that you understand REST APIs, you can build your own with Node.js and Express, or explore how to handle the asynchronous nature of API calls with Promises and Async/Await.