DEV Community

Henrique Leite
Henrique Leite

Posted on • Originally published at henriqueleite42.com on

Should I do it in the client or the server?

When working on interactive websites, we constantly face the challenge of defining if something should be executed on the server or in the client, but how do we decide it? This article will help you know how.

This article does not applies to SPAs.

Different types of web apps

We have two types of web apps: Offline-first and Online-first.

Offline-first apps are the ones where you need an insanely fast response for the user. They are extremely interactive. Good examples are Notion (or any WYSIWYG editors), Google Calendar, Trello, and Google Sheets.

Online-first apps are the most common cases, all apps that don't need to be offline-first should be online-first. They rely on the server's responses to execute things and show the results to the users only after it.

For offline-first apps, you must execute everything on the client BEFORE executing it on the server, this way you don't have to deal with the server's latency, so it's a solved case.

Now, online-first apps, are the real deal. They prompt us with the challenge of deciding where to execute something.

The criteria

To determine if you should execute something in the client or on the server, you need to ask yourself the following questions:

  • Does the user will lose something if the power goes out?

  • If they lose something, will it be extremely important?

Now, let's see how to apply these questions.

Our example

Let's have a web app with a sidebar that displays the username of the currently logged user and a page that has a button "edit username button":

When clicking on the "edit username button", a modal shows up:

And after the username is successfully updated, the username on the sidebar changes, and a success toast appears on the top right corner of the page:

This is not a real web app, I created these images using Photopea

State

Notice that we have multiple states here:

  • The user's username is a state

  • The open/closed modal is a state

  • The toast open/closed and the message are states

The client should only manage short-lived, unimportant states, that control mostly visual things.

The server should have no state, but should be able to operate with states:

  • query string

  • sessions

  • cookies

In summary, avoid states as best as you can, but be aware that we rarely can avoid them, so we must learn how to deal with them.

Opposite poles

We have multiple ways of doing it, and I'll tell you the 2 cases: One that does all the changes through the client and the other that does all the changes through the server.

All client

Initial load:

  • The page loads with all the elements, but the username is empty

  • Does a request to the server's API to get the username

  • Changes the HTML using JS with the data returned by the server's API

The user clicks on the "edit username button" button:

  • It triggers a JS function that opens the modal (removing a hidden class, creating it, and appending it to the HTML, it doesn't matter)

The user inserts his new username and clicks on the "submit" button:

  • The client makes a request on the server's API and waits for it to succeed

  • Then update the HTML text of the username with the new username

  • Closes the modal

  • Makes the toast appear with the message "username successfully updated!" and disappears after some seconds.

All server

Initial load:

  • Get username from session / JWT / database / doesn't matter

  • Returns the page already completed with the username

The user clicks on the "edit username button" button:

  • Redirects the user to another page, that has the modal open and the input

  • Because the username still appears on the sidebar, it will have to get the username again

The user inserts his new username and clicks on the "submit" button:

  • Submits a form that makes a POST request to a route in the server, that changes the username and redirects to another page

  • The new page has the modal closed, the new username, and a toast rendered

Why do both cases suck

Let's see why none of the cases work very well to create a good user experience:

All client

For the ones that only ever worked with React/SPAs, this seems like the only way to do things, and nothing seems wrong, but let's see the drawbacks that this solution has for the user experience:

  • The user will see a glitch on their username in the bottom left corner since it has to be loaded after the page loads

  • It's hard to isolate the things and reproduce the steps to get to the "new username" state (in this case, make the success toast show up)

All server

In this case, I think that it's easier for everyone to see why this approach is bad:

  • You will need to copy the page with the "update username button", with the addition of a modal on top of it. Increases nonsense code duplication.

  • As we can't use any client-side interactions, the toast will stay there for as long as the user stays on the page

Uh, but what about the latency?? If you need to worry about latency in 2024, you probably are doing something wrong. Please, reconsider where are you hosting your servers, how you are delivering your content, and what are your routes doing. Read this article from the Yahoo guys, they rule.

Balance

The best approach is neither all server-side nor all client-side, but a mix of both. And we need to balance this properly.

The correct flow

Initial load:

  • [server] Get username from session / JWT / database / doesn't matter

  • [server] Returns the page already completed with the username

The user clicks on the "edit username button" button:

  • [client] It triggers a JS function that opens the modal (removing a hidden class, creating it, and appending it to the HTML, it doesn't matter)

The user inserts his new username and clicks on the "submit" button:

  • [server] Submits a form that makes a POST request to a route in the server, that changes the username and redirects to the same page, but this time using a query param toastJSON

  • [server] The route will then check if toastJSON is present in the query params, and in addition to the page, will also return a toast without JS.

  • [client] The client will have an window.onload event, that searches for toast elements and applies the necessary JS to them.

Now the user never will lose an important toast nor have glitches in the UI.

It's a lot easier to test if the UI is working: all of our state is shortlived and has no impact if it's lost.

States

Let's review our states and see which part of our system is controlling them:

  • [server] The user's username is a state

  • [client] The open/closed modal is a state

  • [server + client] The toast open/closed and the message are states

Conclusion

Mixing client and server actions helps us to develop more stable and testable applications, without having to manage complex unreproducible states.

Thanks for reading.

Top comments (1)

Collapse
 
nilson_jay_34f8420abfd795 profile image
Y.Schiff

Great