Stop 17 · System Design for Full-Stack AppsTopics · 1/7

Client-Server Architecture Trade-offs

Part of the Full Stack Developer Roadmap

In short

Every feature asks: should this logic run in the browser or on the server? Get it wrong one way and the app feels slow. Get it wrong the other way and users can cheat.

Deciding how much logic lives on the client vs the server — more client logic means a snappier UI but more exposed code; more server logic means better security and control but more round trips.

The purple blob fills pani puris at a stall next to a self-serve tray of puris and fillings.

Think of it like this

Pani puri stall vs DIY chaat tray

At a pani puri stall, the bhaiya fills every puri himself. Slower, but he controls the masala. A DIY tray is instant, but you could fill ten puris with only aloo. Fast and trusting, or slower and in control.

Watch it happen

Where should this logic live?

In the browser

  • Instant feedback, no round trip
  • Code and data visible to anyone
  • Varies with the user's phone

On the server

  • Trusted: users can't edit it
  • Costs a network round trip
  • Same speed on every device

Put UX logic (filters, form hints, animations) in the browser. Put anything about money, permissions or secrets on the server, even if you also show it in the browser.

The big ideas

What you need to know

The browser is public

Anyone can open DevTools, read your JavaScript and change any request. Never trust a price, role or discount sent by the client.

Round trips cost time

Each call to the server can add 100 ms or more on Indian mobile networks. Validate simple things in the browser too.

Do both when it matters

Check a form in the browser for speed, then check it again on the server for safety. Share the rules if you can.

Think about weak phones

Heavy work in the browser drains a budget phone. Big data crunching belongs on the server.

See it in code

Walk through the code

tsx
1// Client: a quick estimate so the cart feels instant2export function CartTotal({ items }: { items: { price: number; qty: number }[] }) {3  const estimate = items.reduce((sum, i) => sum + i.price * i.qty, 0);4  return <p>Approx. ₹{estimate}</p>;5}6 7// Server: the real total, using prices from the database8app.post("/api/checkout", async (req, res) => {9  const ids = req.body.items.map((i: { id: string }) => i.id);10  const products = await db.product.findMany({ where: { id: { in: ids } } });11  const total = req.body.items.reduce((sum: number, i: { id: string; qty: number }) => {12    const p = products.find((x) => x.id === i.id);13    return sum + (p ? p.price * i.qty : 0);14  }, 0);15  res.json({ total });16});

1/3Client-side maths for a snappy UI. Fine to show, never fine to charge from.

The browser shows a fast estimate. The server ignores any price the browser sends and recalculates from the database, so editing the request gains nothing.

Save yourself the pain

Common mistakes

Oops

Hiding the admin button in React and calling that security.

Do this instead

Hide it for neatness, but check the user's role on the server for every admin API call.

Oops

Sending the whole table to the browser and filtering there, because it's 'easier'.

Do this instead

Filter and paginate on the server. Send only what the screen shows, and nothing private.

Your mission

Draw the trust line

~20 min

Quick check

A coupon code is checked only in your React code before calling /api/order. What's the risk?

Go deeper

  1. 1List a few features and decide where each piece of logic should live and why.
  2. 2Consider what should never be trusted to run only on the client (pricing calculations, permissions).
  3. 3Recognize this trade-off shifts with frameworks like Next.js that blend the two.

More topics at this stop

Stuck on this topic? Ask an Insider

Get 1:1 guidance from people who've walked this exact path — on the InsideEdge app.

Download
InsideEdge