Full-Stack Data Flow
Part of the Full Stack Developer Roadmap
In short
'My todo didn't save' could mean the form, the fetch, the route, the validation or the SQL broke. Knowing the full path of one piece of data tells you where to look.
Tracing a single piece of data's entire journey — user input, frontend state, API request, backend validation, database write, response, and UI update — the mental model that ties the whole stack together.

Think of it like this
An answer sheet's round trip
You write your answer sheet, the invigilator collects it, the evaluator checks it, marks go into the register, and the result comes back to you. Lose it at any step and you get no marks. Data in your app makes the same trip: form, request, validation, database, response, screen.
Watch it happen
Saving one todo, end to end
types 'Buy milk' + Enter
1/7The form's onSubmit fires. React is holding the text in state.
The big ideas
What you need to know
Validate on the server
Browser checks are for convenience. Anyone can skip them with curl, so the API must validate every input again.
The database owns the truth
Ids, timestamps and defaults come from the database. Use what the API returns, not what you guessed in the browser.
One shape on both ends
Share the Todo type or schema between frontend and backend, so a renamed field breaks the build, not production.
Debug by following the data
Check each hop in order: React state, the Network tab request, the server log, then the table itself.
See it in code
Walk through the code
1// server.ts (Express)2const TodoInput = z.object({ title: z.string().trim().min(1).max(200) });3 4app.post("/api/todos", async (req, res) => {5 const parsed = TodoInput.safeParse(req.body);6 if (!parsed.success) return res.status(400).json({ error: parsed.error.issues });7 const { rows } = await pool.query(8 "INSERT INTO todos (title) VALUES ($1) RETURNING id, title, done",9 [parsed.data.title],10 );11 res.status(201).json(rows[0]);12});13 14// TodoForm.tsx (React)15const res = await fetch("/api/todos", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ title }) });16if (res.ok) setTodos((prev) => [...prev, await res.json()]);1/4Zod checks the body. Bad input gets a 400 with the reasons, and nothing touches the database.
The server half needs app.use(express.json()) so req.body is parsed. The $1 placeholder keeps the title safe from SQL injection.
Save yourself the pain
Common mistakes
Oops
Adding the todo to the list with a made-up id before the API replies.
Do this instead
Use the row the API returns. If you want instant UI, that's optimistic updating, and you must roll back on failure.
Oops
Validating only in the React form.
Do this instead
Validate in the form for a nice experience and again on the server for safety. The server check is the one that counts.
Your mission
Trace a todo
Quick check
A user adds a todo and sees it on screen, but after a refresh it's gone. Where is the bug most likely?
Go deeper
- 1Pick one feature and diagram every step data takes from click to database and back.
- 2Identify where validation happens at each layer and why it's duplicated (client UX, server security).
- 3Debug a full-stack bug by tracing this same path, layer by layer.
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.