How does HTTP work?
Table of Contents
Understanding HTTP
HTTP (Hypertext Transfer Protocol) is a protocol for the web, used to define how data is transferred between a browser (client) and a backend server. HTTP is stateless, meaning each request is independent and does not maintain any state or memory of previous interactions.
HTTP/1.0
HTTP/1.0 requires a new connection for each resource transfer, resulting in high latency due to the need to establish a connection each time. Each transfer incurs two round-trip times (RTTs), slowing down complex web pages.
HTTP/1.1
HTTP/1.1 introduced persistent connections, enabling multiple file transfers over a single connection. Request pipelining allowed clients to send multiple requests without waiting for each response. However, it is still affected by head-of-line blocking, which causes delays if one resource took longer to load.
HTTP/2
HTTP/2 helps to mitigate head-of-line blocking with multiplexing, which allows data frames of multiple resources to be interleaved over a single connection. This means that if one resource's transfer is slow, it will not block the transfer of other resources. Browsers can start receiving data frames for different resources concurrently. We no longer have to wait for the resource to be fully transferred before the transfer of other resource. This prevents blocking and enables more efficient loading of web pages.
Making HTTP Requests
So how do we make a HTTP request to the backend server? We can do so using libraries likeaxios or the built-in fetch API. These tools allow us to send data to the server in formats like JSON, depending on what the backend server is configured to receive.
JSON (JavaScript Object Notation) is a lightweight format used to transmit data between client and server. Heres an example of a JSON body you might send in an HTTP request:
1
2 {
3 "name": "Alice",
4 "age": 25,
5 "isStudent": false,
6 "hobbies": ["reading", "traveling"]
7}
8 Using fetch, a sample HTTP POST request might look like this:
1const data = {
2 name: "Alice",
3 age: 25,
4};
5
6fetch("https://example.com/api", {
7 method: "POST",
8 headers: {
9 "Content-Type": "application/json", // Specify JSON format
10 },
11 body: JSON.stringify(data), // Convert JavaScript object to JSON string
12})
13.then(response => response.json())
14.then(data => console.log(data))
15.catch(error => console.error(error));
16When using fetch, the response we receive is a Response object. This object contains metadata such as headers, status codes, and the body of the response. However, the body is not directly available as a JSON object—it is provided as a readable stream.
The response.json() method is used to read the stream and parse it into a usable JavaScript object.
Using Axios, a sample HTTP POST request might look like this:
1import axios from "axios";
2
3const data = {
4 name: "Alice",
5 age: 25,
6};
7
8axios.post("https://example.com/api", data, {
9 headers: {
10 "Content-Type": "application/json", // Inform the server that you're sending JSON
11 },
12})
13.then(response => console.log(response.data))
14.catch(error => console.error(error));
15When making HTTP requests using Axios, the response object contains several pieces of information about the request, such as the status code, headers, and the actual response body. The data property of the response object holds the parsed body of the servers response.
By calling response.data, we can directly access the content returned by the server without worrying about parsing it manually. This makes it more convenient to focus on the actual data being retrieved, while the other properties of the response object, such as status codes and headers, can be accessed separately if needed.