This post walks through how I locked down the AuthForm contract in a React and TypeScript app deciding what the frontend guarantees before the backend enters the room.
Click here for Act 4 · Scene 1
Table of Contents
- Overview
- What This Scene Focuses On
- One Form, Two Intentions
- Light Validation
- Shape of the Payload: What the Backend Will Receive
- Mental Model
- Why this scene matters
Act 4 · Scene 2 : Stabilizing the Frontend Contract
In this scene, I slow the momentum on purpose.
Instead of rushing into backend work, I stabilize the frontend contract that authentication will depend on.
Overview
Now that routing works, and pages actually show up when you click them, the temptation is obvious:
“Let’s spin up the backend.”
yaaaaaaaaaaaaaaay!!!
News flash
I didn’t.🤣🤣
Why?
Because before a backend exists, the frontend needs to answer a simple question honestly: What exactly am I sending you?
Authentication is the first true dependency between frontend and backend. If this boundary is unclear, everything else down the road becomes fragile. So, instead of jumping straight into API calls, I focused this scene on stabilizing the AuthForm contract.
This scene is not about authentication working.
It’s about authentication being well defined.
What This Scene Focuses On
- Light but intentional input validation
- Clear separation between login and registration modes
- Explicit payload shapes the backend can rely on
- Placeholder submit handlers to simulate real submission behavior
- No networking yet. No backend assumptions.
One Form, Two Intentions
The AuthForm operates in two modes, controlled by a single piece of state:
const [haveAccount, setHaveAccount] = useState(false);
This boolean determines the intention of the form:
- Login mode : email and password
- Register mode : name, email and password
From a backend perspective, this distinction matters. Each mode produces a different but predictable payload.
Light Validation
The goal here is not to send meaningless requests.
Login validation
if (
enteredEmail.trim().length === 0 ||
enteredPassword.trim().length < 6
) {
return console.log("Fill out the inputs");
}
This ensures that login requests meet a minimum bar of validity before leaving the browser.
Registration validation
if (enteredName.trim().length < 3) {
return console.log("name should be at least 3 characters long");
}
if (enteredEmail.trim().length === 0) {
return console.log("please enter valid email address");
}
if (enteredPassword.trim().length < 6) {
return console.log("password must be at least 6 characters long");
}
These validations are similar to those of the login. They serves the same purpose.
Also, I am keeping validation deliberately light here.
This validation here:
- Stops empty submissions
- Stops obvious mistakes
- Reduces useless backend calls
That’s what makes it lightweight
It doesn’t enforce security; that responsibility belongs to the server.
Shape of the Payload: What the Backend Will Receive
Once validation passes, the form produces a clear payload.
Registration payload
const userData: User = {
name: enteredName,
email: enteredEmail,
password: enteredPassword,
};
Login payload
const userData:User = {
email: enteredEmail,
password: enteredPassword,
};
These payloads are what would be sent to the server.
The interface below serves as a form of map, or should I say guide, for the payloads:
export interface User {
name?: string;
email: string;
password: string;
}
Mental Model
Right now:
- The frontend knows what it’s sending
- The backend can trust the shape of incoming data
- Invalid intent is stopped early
Why This Scene Matters
This scene shows that I:
- Understand authentication as infrastructure, not just UI
- Build in a way that backend engineers can immediately work with
- Don’t rush into APIs without contracts
Thanks for reading.
Let’s move on to the Next Act.
We in the Building…
Building in Progress…
Top comments (0)