{}const=>[]async()letfn</>var
DevelopmentWeb

REST API: A Complete Guide for Developers

A complete guide to the REST API for developers. The article covers the basic principles of REST, HTTP methods, API structure, authentication, and best practices. Practical examples in JavaScript and Node.js with step-by-step instructions for creating your own REST API.

К

Kodik

Author

9 min read

What is REST?

REST (Representational State Transfer) is an architectural style for building distributed systems proposed by Roy Fielding in 2000 in his doctoral dissertation. REST is not a protocol or standard, but rather a set of principles and constraints that a system must follow.

API (Application Programming Interface) is an interface for interaction between programs. REST API, respectively, is an API built in accordance with the REST principles.

In simple words, REST API is a way of organizing communication between a client and a server via HTTP protocol, where each resource (data) has a unique address (URL), and it can be accessed using standard HTTP methods.

🔥 100,000+ students already with us

Tired of reading theory?
Time to code!

Kodik — an app where you learn to code through practice. AI mentor, interactive lessons, real projects.

🤖 AI 24/7
🎓 Certificates
💰 Free
🚀 Start learning
Joined today

Basic principles of REST

REST is based on six key principles that define the architecture of the system.

1. Client-Server

The architecture is divided into a client that sends requests and a server that processes these requests and returns responses. This separation allows the client and server to evolve independently of each other.

2. Stateless

Each request from the client to the server must contain all the information necessary to understand and process the request. The server does not store information about the client's status between requests. If authentication is required, the token is transmitted with each request.

3. Cacheable

Server responses must explicitly indicate whether they can be cached. This improves system performance by reducing the number of requests to the server.

4. Uniform Interface

This is a key principle of REST, which simplifies the architecture of the system. It includes four aspects: resource identification through URI, resource manipulation through representations, self-describing messages, and HATEOAS (hypermedia as an application state engine).

5. Layered System

The client cannot determine whether it is connected directly to the end server or to an intermediate node. This allows you to add load balancers, caches, and other intermediate components without changing the client code.

6. Code on Demand

This is the only optional principle. Servers can temporarily extend the functionality of the client by passing executable code, such as JavaScript.

HTTP methods in REST API

The REST API uses standard HTTP methods to perform operations with resources. Each method has a specific purpose:

GET — Receiving data

The GET method is used to read data from the server. It should not change the state of the resource.

// Get a list of all users
fetch('https://api.example.com/users')
  .then(response => response.json())
  .then(data => console.log(data));

// Get a specific user
fetch('https://api.example.com/users/123')
  .then(response => response.json())
  .then(data => console.log(data));

POST — Create a new resource

POST is used to create new resources on the server.

fetch('https://api.example.com/users', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    name: 'Ivan Petrov',
    email: 'ivan@example.com'
  })
})
  .then(response => response.json())
  .then(data => console.log(data));

PUT — Full resource update

PUT replaces the existing resource with entirely new data.

fetch('https://api.example.com/users/123', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    name: 'Ivan Petrov',
    email: 'newemail@example.com',
    age: 30
  })
})
  .then(response => response.json())
  .then(data => console.log(data));

PATCH — Partial update of the resource

PATCH updates only the specified fields of the resource.

fetch('https://api.example.com/users/123', {
  method: 'PATCH',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    email: 'newemail@example.com'
  })
})
  .then(response => response.json())
  .then(data => console.log(data));

DELETE — Deleting a resource

DELETE is used to delete a resource from the server.

fetch('https://api.example.com/users/123', {
  method: 'DELETE'
})
  .then(response => {
    if (response.ok) {
      console.log('User removed');
    }
  });

REST API structure

The correct URL structure in the REST API is critical to understanding and using the API.

Resources and collections

In REST, everything is a resource. Resources are grouped into collections:

GET    /users           - Получить список пользователей (коллекция)
GET    /users/123       - Получить конкретного пользователя (ресурс)
POST   /users           - Создать нового пользователя
PUT    /users/123       - Обновить пользователя
DELETE /users/123       - Удалить пользователя

Invested resources

Nested URLs are used for related resources:

GET    /users/123/posts           - Все посты пользователя
GET    /users/123/posts/456       - Конкретный пост пользователя
POST   /users/123/posts           - Создать пост для пользователя
DELETE /users/123/posts/456       - Удалить пост пользователя

Filtering and sorting

Use query parameters for filtering, sorting, and pagination:

GET /users?role=admin                    - Фильтрация по роли
GET /users?sort=name&order=asc          - Сортировка по имени
GET /users?page=2&limit=20              - Пагинация
GET /users?search=иван                   - Поиск

HTTP response statuses

The REST API uses standard HTTP status codes to inform the client about the result of the request.

Successful responses (2xx)

  • 200 OK — request completed successfully (for GET, PUT, PATCH)

  • 201 Created — resource successfully created (for POST)

  • 204 No Content — the request was successful, but there is no content to return (often for DELETE)

Client errors (4xx)

  • 400 Bad Request — invalid request (for example, invalid JSON)

  • 401 Unauthorized - authentication required

  • 403 Forbidden — access denied (authenticated, but no permissions)

  • 404 Not Found — resource not found

  • 409 Conflict — conflict (for example, a user with this email already exists)

  • 422 Unprocessable Entity — validation failed

Server errors (5xx)

  • 500 Internal Server Error — internal server error

  • 503 Service Unavailable — service temporarily unavailable

Data format

REST API usually works with JSON (JavaScript Object Notation), although XML can also be used.

JSON response example

{
  "id": 123,
  "name": "Ivan Petrov",
  "email": "ivan@example.com",
  "created_at": "2024-01-15T10:30:00Z",
  "posts": [
    {
      "id": 1,
      "title": "First post",
      "published": true
    }
  ]
}

Example of a JSON error

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Data validation error",
    "details": [
      {
        "field": "email",
        "message": "Incorrect email format"
      }
    ]
  }
}

Authentication and security

REST API often requires authentication to access protected resources.

JWT (JSON Web Token)

The most popular authentication method for REST API:

// Getting a token when logging in
fetch('https://api.example.com/auth/login', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    email: 'user@example.com',
    password: 'password123'
  })
})
  .then(response => response.json())
  .then(data => {
    // Saving token
    localStorage.setItem('token', data.token);
  });

// Using a token for secure requests
fetch('https://api.example.com/users/me', {
  headers: {
    'Authorization': `Bearer ${localStorage.getItem('token')}`
  }
})
  .then(response => response.json())
  .then(data => console.log(data));

API Keys

Simple method for service-oriented APIs:

fetch('https://api.example.com/data', {
  headers: {
    'X-API-Key': 'your-secret-key'
  }
})
  .then(response => response.json())
  .then(data => console.log(data));

API versioning

As the API evolves, it is important to maintain backward compatibility. There are several approaches to versioning:

URL Path Versioning

https://api.example.com/v1/users
https://api.example.com/v2/users

Header Versioning

fetch('https://api.example.com/users', {
  headers: {
    'Accept': 'application/vnd.example.v2+json'
  }
})

Query Parameter Versioning

https://api.example.com/users?version=2

Practical example: creating a simple REST API on Node.js

Let's create a simple REST API to manage the task list.

const express = require('express');
const app = express();

app.use(express.json());

// Temporary data storage
let tasks = [
  { id: 1, title: 'Explore the REST API', completed: false },
  { id: 2, title: 'Create a project', completed: false }
];

let nextId = 3;

// GET - Get all tasks
app.get('/api/tasks', (req, res) => {
  res.json(tasks);
});

// GET - Get a specific task
app.get('/api/tasks/:id', (req, res) => {
  const task = tasks.find(t => t.id === parseInt(req.params.id));
  
  if (!task) {
    return res.status(404).json({ 
      error: 'Task not found' 
    });
  }
  
  res.json(task);
});

// POST - Create a new task
app.post('/api/tasks', (req, res) => {
  const { title } = req.body;
  
  if (!title) {
    return res.status(400).json({ 
      error: 'Task name is required' 
    });
  }
  
  const newTask = {
    id: nextId++,
    title,
    completed: false
  };
  
  tasks.push(newTask);
  res.status(201).json(newTask);
});

// PUT - Update task
app.put('/api/tasks/:id', (req, res) => {
  const taskIndex = tasks.findIndex(t => t.id === parseInt(req.params.id));
  
  if (taskIndex === -1) {
    return res.status(404).json({ 
      error: 'Task not found' 
    });
  }
  
  const { title, completed } = req.body;
  
  tasks[taskIndex] = {
    id: parseInt(req.params.id),
    title: title || tasks[taskIndex].title,
    completed: completed !== undefined ? completed : tasks[taskIndex].completed
  };
  
  res.json(tasks[taskIndex]);
});

// DELETE - Delete task
app.delete('/api/tasks/:id', (req, res) => {
  const taskIndex = tasks.findIndex(t => t.id === parseInt(req.params.id));
  
  if (taskIndex === -1) {
    return res.status(404).json({ 
      error: 'Task not found' 
    });
  }
  
  tasks.splice(taskIndex, 1);
  res.status(204).send();
});

const PORT = 3000;
app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});

Best Practices in REST API development

1. Use nouns, not verbs

Good:

GET /users
POST /users

Bad:

GET /getUsers
POST /createUser

2. Use the plural form for collections

GET /users (а не /user)
GET /posts (а не /post)

3. Return the correct HTTP codes

Do not return 200 OK for all responses. Use the appropriate status codes.

4. Provide detailed error messages

{
  "error": {
    "code": "INVALID_EMAIL",
    "message": "Invalid email address provided",
    "field": "email",
    "value": "invalid-email"
  }
}

5. Use pagination for large collections

app.get('/api/users', (req, res) => {
  const page = parseInt(req.query.page) || 1;
  const limit = parseInt(req.query.limit) || 10;
  const startIndex = (page - 1) * limit;
  const endIndex = page * limit;

  const results = {
    data: users.slice(startIndex, endIndex),
    pagination: {
      page,
      limit,
      total: users.length,
      totalPages: Math.ceil(users.length / limit)
    }
  };

  res.json(results);
});

6. Document your API

Use tools like Swagger/OpenAPI to document the API.

7. Use HTTPS

Always use HTTPS for data transmission, especially when working with sensitive information.

8. Implement Rate Limiting

Limit the number of requests from a single client to protect against abuse.

REST vs GraphQL vs gRPC

REST is not the only way to build an API. Here is a brief comparison:

REST is suitable for most standard web applications, easy to understand and implement, has broad support and excellent caching.

GraphQL useful when the client needs flexibility in choosing data, allows you to get everything in one request and avoid over-fetching or under-fetching data.

gRPC optimal for microservice architecture, high-performance systems and internal APIs, uses a binary protocol and is faster than REST.

Tools for working with REST API

API testing

  • Postman — a popular tool for testing APIs with a graphical interface

  • Insomnia — an alternative to Postman with a minimalist interface

  • curl — console utility for HTTP requests

# Example of using curl
curl -X GET https://api.example.com/users
curl -X POST https://api.example.com/users \
  -H "Content-Type: application/json" \
  -d '{"name":"Ivan","email":"ivan@example.com"}'

Client libraries

JavaScript/TypeScript:

  • Fetch API (built-in)

  • Axios

  • Got

Python:

  • requests

  • httpx

PHP:

  • Guzzle

  • cURL

Conclusion

REST API is a fundamental technology of modern web development that provides a simple and standardized way of interaction between the client and the server. Understanding the principles of REST, using HTTP methods and status codes correctly, and following best practices will help you create high-quality, which are scalable and easy to maintain.

Start with simple projects, gradually adding complexity, and don't forget about documentation and testing. REST API is a skill that will remain relevant for many years to come and will open the doors to the world of modern development.

Join the educational platform Code, where you will find structured courses on JavaScript, Node.js, Python and other modern technologies.

Our friendly community of developers in Telegram always ready to help with questions, share experiences and support you on your way to becoming a professional programmer!

🎯Stop procrastinating

Liked the article?
Time to practice!

In Kodik, you don't just read — you write code immediately. Theory + practice = real skills.

Instant practice
🧠AI explains code
🏆Certificate

No registration • No card