Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Frontend, Backend, Database, and Real-Time Events

The previous chapter followed a message through Chahua. This chapter uses that same journey to answer a different question: which part of the application is responsible for each step?

Clear responsibilities make an application easier to understand and change. They also help you decide where to begin looking when something does not work as expected.

Three Kinds of State

State is information an application needs to remember. It helps to separate state into three broad kinds:

  • Interface state describes what is happening on one screen, such as which chat is open, what text is in the message box, or whether a request is loading.
  • Local application state is the client’s current view of shared information, such as the messages it has loaded.
  • Durable shared state is information saved by the backend in the database, such as the accepted messages in a chat.

The client’s local state is useful, but it may be incomplete or briefly out of date. A client might have loaded only the newest messages, missed events while offline, or still be waiting for a request to finish. The backend and database determine the durable shared result.

The Client: Interaction and Presentation

A client gives a person a way to use Chahua. The PWA runs in a web browser, while the Flutter application is another client with its own interface and implementation.

For the PWA, the frontend is responsible for work such as:

  • showing chats, messages, forms, and buttons;
  • collecting input, including a message draft;
  • representing temporary states such as loading, sending, or an error;
  • sending requests in response to a person’s actions;
  • receiving data and events and updating the screen; and
  • keeping enough local state to provide a responsive interface.

Suppose you type Hello into a message box. That draft belongs to the client until you send it. The frontend can display it immediately because no other user needs to see an unfinished draft.

The client should not have final authority over shared rules. It may hide a button when it knows a person lacks permission, but the backend must still check the permission. A different or modified client could send the request without using that button.

The HTTP API: A Shared Contract

When the person selects Send, the client needs to ask the backend to create the message. It does this through Chahua’s HTTP API.

An API is a contract between software components. For an HTTP API, that contract describes details such as:

  • which route and HTTP method to use;
  • what information the request must contain;
  • how identity is provided;
  • what data a successful response contains; and
  • which errors the backend may return.

Every Chahua client can use the same backend contract, even though the clients may be written in different languages and display different interfaces. Learning how the PWA makes a request therefore gives you a model for understanding how the Flutter client or a future client can perform the same action.

The contract works in both directions. If the backend changes the shape or meaning of a request or response, clients may also need to change. Later chapters will show where these contracts appear in the code; for now, remember that the API is the boundary through which a client asks for shared work to be done.

The Backend: Rules and Coordination

The backend receives requests from every client and coordinates the shared work. When it receives the request to send Hello, it may need to:

  1. Authenticate the request by determining who sent it.
  2. Authorize the action by checking whether that person may send messages in this chat.
  3. Validate the input by checking that required information has an acceptable form.
  4. Apply Chahua’s business rules, meaning the rules that define how the application behaves.
  5. Read or change information in PostgreSQL.
  6. Return either a result or an error using the API contract.
  7. Coordinate follow-up work, such as real-time events or notifications.

Authentication and authorization are related but different. Authentication answers, “Who is making this request?” Authorization answers, “May this person perform this action?”

These checks belong in the backend because it is the shared boundary used by all clients. A frontend check can improve the experience, but it cannot safely replace a backend check.

PostgreSQL: Durable Shared State

Chahua uses a PostgreSQL database to store durable information such as users, chats, and messages. Durable means that the information remains available after a request finishes, a browser closes, or a backend process restarts.

Clients do not connect directly to PostgreSQL. They ask the backend for information, and the backend applies permissions and application rules before reading or changing the database.

For the message journey, the database is where Hello becomes an accepted, durable message. Until that change succeeds, the application must be prepared for the operation to fail. The backend might reject the request, or the database operation might not complete.

It is useful to think of PostgreSQL as the durable source of truth for this shared information. A message displayed in one client’s local state is a view of that information, not an independent authoritative copy.

Real-Time Events: Announcing a Change

After the message is stored, other connected clients need to learn about it. Chahua uses WebSocket connections to deliver real-time events such as an announcement that a message was created.

A WebSocket event is not the durable message itself and does not replace the database. It tells a client that shared state changed and gives it information needed to update its local view.

The order matters. The backend should announce a successful change only after the corresponding database change succeeds. Otherwise, a client could display a message that was never durably stored.

Connected clients can use the event to update promptly without repeatedly asking whether anything changed. Each client then updates its own local state and renders the new message in its own interface.

Reconnecting and Catching Up

Real-time connections are not permanent. A laptop can sleep, a phone can lose its network connection, or a backend process can restart. Events sent while a client is disconnected may never reach that client.

For this reason, WebSockets do not replace HTTP requests. A client commonly uses:

  • HTTP to load an initial or authoritative view of data and to recover missed information; and
  • WebSocket events to apply new changes promptly while connected.

When a client reconnects, it may need to request messages again or request everything after the last item it knows about. The exact recovery mechanism belongs in later implementation chapters. The important concept is that the client must be able to rebuild its local view from durable backend data rather than assuming it received every event.

Supporting Systems

The client, backend, and PostgreSQL form the core path, but a production chat application needs other services. Chahua also has supporting systems for concerns such as:

  • object storage, which stores attachment data that does not belong directly in a database row;
  • push notifications, which can alert a device when its client is not actively connected; and
  • search, which helps find messages efficiently.

These systems have specialized jobs. They support the core application but do not replace its responsibility boundaries. Clients still go through the backend, backend rules still control shared actions, and PostgreSQL remains the durable source for the application’s primary records.

Put the Responsibilities Together

The complete message journey now looks like this:

  1. The PWA holds the unfinished draft in interface state.
  2. The PWA sends an HTTP request defined by the API contract.
  3. The backend authenticates the sender, authorizes the action, validates the request, and applies Chahua’s rules.
  4. PostgreSQL stores the durable message.
  5. After that change succeeds, the backend returns a response and sends real-time events.
  6. Connected clients update their local state and screens.
  7. A client that was disconnected later uses HTTP to catch up from durable data.

This model will help you trace other features. Ask what belongs only to one interface, what crosses the API boundary, which rules require shared enforcement, what must be stored durably, and how other clients learn about a change.

Exercise: Who Owns This Work?

For each task below, decide which part should have the primary responsibility: the client, HTTP API contract, backend, PostgreSQL, or real-time delivery. Some tasks involve more than one part, but try to identify the owner.

  1. Keep the text of an unsent message visible while a person types.
  2. Define the data a client must provide to create a message.
  3. Check whether the sender belongs to the chat.
  4. Preserve an accepted message after everyone closes the application.
  5. Tell another connected client that the message was created.
  6. Show a temporary spinner while the request is in progress.
  7. Recover messages missed while a device was offline.
Suggested answers
  1. The client owns the unsent draft.
  2. The HTTP API contract defines the request.
  3. The backend checks membership and permission.
  4. PostgreSQL stores the durable message, coordinated by the backend.
  5. Real-time delivery announces the committed change.
  6. The client represents the temporary loading state.
  7. The client uses the HTTP API to ask the backend for durable data from PostgreSQL.

You now have a responsibility map for the application. The repository tour later in this book will connect this map to directories and files, and the frontend and backend learning paths will explain how each responsibility is implemented.