DevPath · Learn to code ESPTEN

Connect front and back

States on the client: loading, error, and data

A request is not instant

Requesting data from an API takes time: it travels over the network, the backend works, it comes back. During that time your interface cannot go blank. Every API call goes through three states and the UI must account for all three:

  1. Loading — the request went out, there is still no response. Show a spinner or a skeleton.
  2. Error — the response failed (network down, 404, 500). Show a message and, if appropriate, a retry button.
  3. Data — the correct response arrived. Render the content.
async function load(api) {
  // state: loading
  try {
    const data = await api("/users");
    return { state: "data", data };   // state: data
  } catch (e) {
    return { state: "error", error: e.message }; // state: error
  }
}

Why try/catch matters

If you do not catch the error, a downed API breaks the interface: the promise is rejected, the render fails, and the user sees a broken screen without knowing what happened. Wrapping the call in try/catch turns a network failure into a state you know how to paint.

Adapting the response to the UI

The shape the API returns is rarely the one your interface needs to paint. It is good practice to adapt (map) the response to the shape the UI consumes, in a single layer, instead of scattering user.personal_data.name all over the application:

function adaptUser(u) {
  return { id: u.id, name: u.full_name, active: u.status === "ACTIVE" };
}

That way, if the backend contract changes, you only tweak the adapter and the rest of the UI stays intact.

In the next module you will see the other piece of connecting front and back: the authentication with tokens (JWT), so the backend knows who makes each request.

Put this into practice

DevPath is a hands-on course: you read the theory here; in the app you put it into practice with exercises that really run, offline.

Start free in the app →
← The API contract: JSON, states, and CORSView the module →