Chapter 5 ยท CORE

Frontend Data Layer

๐Ÿ“„ 05_frontend_data_layer.md ๐Ÿท Core

Chapter 5: Frontend Data Layer

Welcome to the client side!

In the previous chapters, we built a robust backend:

However, if you visit your server now, you just get raw data. A normal user doesn't want to read JSON text; they want to click buttons and see folder icons.

In this chapter, we will build the Frontend Data Layer. This is the "Messenger" that lives in your web browser. It connects the visual interface (buttons) to the backend API (server).

The Problem: The Language Barrier

The Backend speaks JSON and HTTP Codes (like 200 OK or 403 Forbidden). The User speaks Clicks and Visuals.

We need a translation layer that:

  1. Takes a "Click" (User Action).
  2. Turns it into a network request (Fetch).
  3. Receives the raw data.
  4. Stores it in the browser's memory (State) so the screen can update.

The Solution: The API Client & The Store

Filebrowser (built with Vue.js) divides this job into three parts:

  1. The API Client (api/files.js): A collection of functions that know exactly how to talk to the backend.
  2. The Store (store): The application's "Short-term Memory." It holds data like "Who is logged in?" or "What files are in this folder?"
  3. The Router (router): The map that decides which page to show based on the URL.

Key Concepts

1. The API Client

This is the specialized messenger. Instead of writing "Make a network request to /api/resources" inside every button in our app, we write a function called fetchFiles. The button just calls fetchFiles.

2. The Store (State)

Imagine you are browsing a folder. The application needs to remember the list of files so it can draw them on the screen. It also needs to remember if you are an Admin or a regular user. This "memory" is called the State.

3. The Router

When you change the URL from /login to /files, the whole page doesn't reload. The Router swaps the "Login Component" for the "Files Component" instantly. It also checks if you have the "ticket" (Authentication) to enter that page.


How it Works: A Use Case

Let's look at a classic scenario: The user clicks on the "Documents" folder.

  1. Action: User clicks a folder icon.
  2. Request: The API Client sends GET /api/resources?path=/Documents.
  3. Response: The Server sends back a list of files in JSON.
  4. Update: The Store saves this list.
  5. Reaction: The UI automatically updates to show the new files.

Usage Example: Fetching Files

Let's look at frontend/src/api/files.js. This is how the frontend asks the backend for data.

// frontend/src/api/files.js

export async function fetchFiles(source, path, content = false) {
  // 1. Construct the URL (e.g., /api/resources?path=/photos)
  const apiPath = getApiPath('api/resources', {
    path: path,
    source: source,
    ...(content && { content: 'true' })
  })

  // 2. Perform the network request
  const res = await fetchURL(apiPath)
  
  // 3. Convert response to JSON and return it
  const data = await res.json()
  return adjustedData(data)
}

Explanation:


Code Walkthrough: Modifying Files

Reading is easy. But what about uploading? Uploading requires a progress bar, so the user knows the app hasn't frozen.

Filebrowser uses XMLHttpRequest (an older but powerful browser tool) for uploads because it supports progress monitoring better than the standard fetch in some cases.

// frontend/src/api/files.js

export function post(source, path, content, overwrite, onupload) {
  const apiPath = getApiPath("api/resources", { path, source, override: overwrite });
  const request = new XMLHttpRequest();
  
  // 1. Open the connection
  request.open("POST", apiPath, true);

  // 2. Listen for progress (for the loading bar)
  request.upload.onprogress = (event) => {
      const percent = Math.round((event.loaded / event.total) * 100);
      onupload(percent); // Update the UI
  };

  // 3. Send the file data
  request.send(content);
}

Explanation:


Under the Hood: The Navigation Flow

When you type a URL into the browser, the Router takes over before any data is fetched. It acts as the bouncer.

The Routing Diagram

sequenceDiagram participant U as User participant R as Router participant S as Store (State) participant V as View (Files.vue) U->>R: Go to "/files/my_photos" Note over R: Check Security alt User NOT Logged In R->>S: Check Auth State S-->>R: "No User Found" R-->>U: Redirect to "/login" else User Logged In R->>V: Load "Files" Component V->>U: Display Folder end

Deep Dive: The Router Guard

In frontend/src/router/index.ts, the beforeResolve function runs every time you change pages.

// frontend/src/router/index.ts

router.beforeResolve(async (to, from, next) => {
  // 1. Check if the page requires login
  if (to.matched.some((record) => record.meta.requiresAuth)) {
    
    // 2. If we don't know the user yet, try to validate the current session
    if (!state.user.username) {
      await validateLogin(); 
    }

    // 3. If still not logged in, kick them out
    if (!getters.isLoggedIn()) {
      next({ path: "/login" });
      return;
    }
  }
  // 4. Allowed! Proceed to page.
  next();
});

Explanation:


Organizing the Data: The Store

We've talked about the "Store" (or State). In Filebrowser, this is a central object that holds the truth about the application.

While the file content isn't shown in the snippets provided, frontend/src/store/index.ts initializes this memory bank.

// frontend/src/store/index.ts

import { state } from "./state.js";
import { getters } from "./getters.js";
import { mutations } from "./mutations.js";

// Export the centralized brain of the app
export {
  state,     // The Data (e.g., { user: "admin", theme: "dark" })
  getters,   // Calculated Data (e.g., isLoggedIn = user !== null)
  mutations  // Functions to change Data (e.g., setUser("alice"))
};
  1. State: The raw variables.
  2. Getters: Helper functions to answer questions about the state.
  3. Mutations: The only way to change the state. We never change state.user directly; we call mutations.setCurrentUser(...). This ensures changes are tracked and reactive.

Summary

In this chapter, we built the invisible machinery of the frontend:

  1. The API Layer (api/files.js) translates user actions into HTTP requests (GET, POST, DELETE).
  2. The Store (store/index.ts) acts as the application's memory.
  3. The Router (router/index.ts) manages navigation and protects private pages from unauthenticated users.

Now that we can fetch data and manage our location in the app, we need to actually draw the interface. We need buttons, lists, icons, and layout.

Next Chapter: Frontend View Layer


Generated by Code IQ