Skip to content

The Client-Server Mental Model

You’ve built the backend. Now you’ll build the frontend that talks to it. Before writing any React code, you need a clear mental model of how a client-side app communicates with a server.

In your previous React apps (ZeroBudget, FamilyTree), data lived in localStorage or component state. It was always available, always synchronous, and never left the browser.

With Bulletin, data lives on a server in a database. Accessing it requires:

  1. Sending an HTTP request over the network to your Railway API
  2. Waiting for the server to process and respond
  3. Handling three possible outcomes: loading, success, or error

This is fundamentally different from reading localStorage. The network is slow, unreliable, and asynchronous.

React component
↓ axios.get('https://api.railway.app/posts')
↓ Network request (10ms–2000ms+)
Railway API
↓ Express route handler
↓ SQLite query
↑ JSON response { posts: [...] }
React component
↑ Update state with received data
↑ Re-render with posts displayed

Every data fetch is an async operation with this cycle. Your UI must account for all three states: loading (waiting), success (data arrived), and error (something went wrong).

Your API returns JSON. React receives it as a JavaScript object:

const response = await axios.get('/posts')
// response.data → { posts: [...], total: 42, page: 1 }

TypeScript interfaces describe the shape of this data:

interface Post {
id: number
title: string
body: string
user_id: number
upvotes: number
created_at: string
author_username: string
}
interface PostsResponse {
posts: Post[]
total: number
page: number
limit: number
}

Define these interfaces in a src/types/api.ts file and use them throughout the frontend.

Your React app needs to know where the API is. In development, it’s http://localhost:3000. In production, it’s your Railway URL.

Use Vite environment variables:

src/config.ts
export const API_BASE_URL = import.meta.env.VITE_API_URL || 'http://localhost:3000'

In .env.local (for local development):

VITE_API_URL=http://localhost:3000

In GitHub Actions (for production):

VITE_API_URL=https://your-app.up.railway.app

VITE_ prefix is required for Vite to expose environment variables to the browser.

Terminal window
npm create vue@latest bulletin # This is a React course — use:
npm create vite@latest bulletin -- --template react-ts
cd bulletin
npm install
npm run dev

The frontend is a standard Vite React + TypeScript project — the same scaffold you’ve used in React Foundations.

Think through the Bulletin frontend architecture:

  1. What pages (routes) does Bulletin need? Write them out.
  2. What data does each page need to fetch?
  3. What actions (mutations) does each page need to perform?
  4. Which pages are accessible to anonymous users? Which require authentication?
  • Data lives on the server — every access requires an HTTP request.
  • Every request has three states: loading, success, error — your UI must handle all three.
  • JSON is the data format — define TypeScript interfaces for the API response shapes.
  • VITE_API_URL lets you switch between local and production API endpoints without code changes.