The 5 pillars of every HTTP request

Amin - Jun 21 '23 - - Dev Community

Request

A request, or an HTTP request, is an action that is sent to a server in order to get something, or to send some information. This includes the URL of the server, the headers and the body of the request.

Most of what will be explained will be important for requesting some information, but can also be applied when sending information as well.

Loading

Displaying a loading information for your users is an important step of a request because we never know what could happen on the network, maybe the connection is slow, maybe the server is slowed down because of the numerous requests.

Showing a loader, or a text indicating that the request is still being made is an additional step that can make your application look more professional and is more user friendly than thinking that everyone has a fast internet connection.

You can simulate slowed down request from the Developer Console in your favorite browser such as Firefox or Google Chrome.

Error

Things happen in the network, and we can't control everything beside what is happening inside our code.

The network might be shut down momentarily, or the user has activated airplane mode, or the server has been down for some time. We never know what kind of problem there might be, or when it can happen but we know that there might be some problem and we must account for that.

It is a good practice to account for these thing in the code, especially in JavaScript since sending a request often involves using a Promise, and a promise might be in a rejected state.

You can also simulate an offline connection in your browser from the Developer Console.

Cancelable

If you plan on giving your users data from a remote API, at least provide a way of canceling these requests.

This is a good practice and an added user experience bit in any application since getting a huge payload from a remote server might be costy for users that are on data plans and having the choice is a good way of showing your users that you are considering everyone, even those that cannot afford much data transfer.

Using JavaScript and the Web API Fetch, you can use a signal along with an Abort Controller in order to provide a way of canceling a request.

Validation

Finally, you have sent a request, everything goes according to plan and you receive a successful response. Or is it?

How can you be sure that the server won't change its response in a day, or a week, or a year? Your application might work for a while, but if anyone decide to send an object with a property instead of an array as usual, you might get into trouble because you'll try to iterate over an object instead of an array which is not possible out-of-the-box in JavaScript.

Data validation is an important step, and might as well be mandatory in some case because even if you know what you are doing today and you are the only developer for a frontend & backend application, you might not be alone in a year and people might join the fight and help you.

If you go back from a long vacation and the API has change, at least with data validation you know that this is a case you accounted for and your application won't crash suddenly (and you might even get better errors that will lead you to resolve this error quicker than without data validation).

Also, with data validation, you can rely on languages that are strongly typed like TypeScript to ensure that once this data has been parsed and validated, you are 100% sure you can iterate over it instead of being afraid it might change in a near future.

Example

Here is what a beginner application might look like in React for the example.

import React, { useEffect, useState } from "react";

export const App = () => {
  const [users, setUsers] = useState([]);

  useEffect(() => {
    fetch("https://jsonplaceholder.typicode.com/users").then(response => {
      return response.json();
    }).then(newUsers => {
      setUsers(newUsers);
    });
  }, []);

  return (
    <ul>
      {users.map(user => (
        <li key={user.id}>{user.username}</li>
      ))}
    </ul>
  );
};
Enter fullscreen mode Exit fullscreen mode

As you can see, no loading state, no cancelable request, no accounting for errors nor data validation.

Here is what it might look like with all these things added.

import React, { Fragment, useRef, useEffect, useState, useCallback } from "react";

const isValidUser = input => {
  return typeof input === "object"
    && input !== null
    && typeof input.id === "number"
    && typeof input.username === "string";
}

const isValidUsers = users => {
  if (!Array.isArray(users)) {
    return false;
  }

  if (!users.every(user => isValidUser(user))) {
    return false;
  }

  return true;
}

export const App = () => {
  const [users, setUsers] = useState([]);
  const abortController = useRef(null);
  const [error, setError] = useState(null);
  const [loading, setLoading] = useState(false);

  const cancel = useCallback(() => {
    abortController?.current?.abort();
  }, [abortController]);

  useEffect(() => {
    abortController.current = new AbortController();
    const { signal } = abortController.current;

    setError(null);
    setLoading(true);

    fetch("https://jsonplaceholder.typicode.com/users", {
      signal
    }).then(response => {
      if (response.ok) {
        return response.json();
      }

      return Promise.reject(new Error("Something went wrong"));
    }).then(newUsers => {
      if (!isValidUsers(newUsers)) {
        throw new Error("Wrong response from the server");
      }

      setUsers(newUsers);
    }).catch(error => {
      setError(error);
    }).finally(() => {
      setLoading(false);
    });
  }, []);

  return (
    <Fragment>
      {loading && (
        <small>Loading, please wait...</small>
      )}
      {error && (
        <small>{error.message}</small>
      )}
      <button onClick={cancel}>Cancel</button>
      <ul>
        {users.map(user => (
          <li key={user.id}>{user.username}</li>
        ))}
      </ul>
    </Fragment>
  );
};
Enter fullscreen mode Exit fullscreen mode

Of course no styling has been applied so it looks awful but at least you get the idea.

We added a way of canceling the request, a loading indication to reassure the user, a display of any error and a basic data validation mechanism in order to ensure the data we get is not corrupted or has been changed.

Conclusion

We saw that in order to build reliable applications, there were 5 steps that we must follow whenever we make a request to a server:

  • Send a proper request
  • Display a loading state
  • Display errors if any
  • Make the request cancelable
  • Validate the data we receive

If you manage to follow these steps, you'll build highly reliable applications that are time-tested and sturdy.

This will instantly make your application way better in the eyes of your users.

These concepts are not tied to JavaScript nor React and can be applied to pretty much any language or any framework & library out there as long as you follow these steps.

.
Terabox Video Player