Requests & Validation Patterns - humanbit-dev-org/templates GitHub Wiki
1. User action
- User clicks submit.
-
handleSubmit
runs (connected to the<form onSubmit={handleSubmit}>
).
2. Browser-level validation (HTML attributes)
-
The browser can validate inputs automatically using attributes like
required
,type
,min
,max
,pattern
, andstep
. -
For example,
type="number"
prevents entering non-numeric text, and if anyrequired
inputs are empty, the browser blocks submission before the client-side validation function even runs:<input type="number" required value={fieldA} onChange={(e) => setFieldA(Number(e.target.value))} />
3. Custom client-side validation
- Runs inside
handleSubmit
. - Checks business rules the browser can't (e.g., value โฅ 10, percentage โค 100).
- If invalid โ set errors and stop before sending request.
4. Build the request
- Gather form values into a
payload
(the actual content of a request โ e.g., JSON with form values). - Attach headers (auth, content-type, etc.).
5. Send request
- Use
fetch
to send data to the backend.
6. Handle response (server-side validation within business logic)
- The backend runs its business logic when it receives the request.
- Part of this logic is server-side validation (also called backend validation), which enforces business rules and ensures data integrity.
- If validation fails, it returns errors (e.g., value too high, user not authorized, bad data).
- If everything passes, it accepts the request and continues its logic (e.g., save data, update workflow) and the client proceeds (reload or redirect).
Business rules, business logic, and server-side validation
-
Business rules are the constraints or requirements themselves (e.g., "value must be โฅ 10," "percentage cannot exceed 100," "user must be authorized").
-
Business logic is the code that implements all of those rules. It's broader than just validation, and may include:
- Validation (client-side or server-side) โ making sure inputs are valid and requests follow the rules.
- Processing rules โ calculations or transformations (e.g., computing repayment time).
- Authorization and permissions โ deciding whether a user is allowed to perform an action.
- Workflow/process rules โ what happens after validation succeeds (e.g., update database, trigger an email, move item status).
- Integration rules โ how the system interacts with external services (e.g., payment API, scoring service).
-
Server-side validation (also called backend validation) is one specific part of business logic: it enforces business rules and ensures data integrity on the backend.
7. Handle network failure
- If
fetch
itself throws (fails) โ show "network error."
const handleSubmit = async (event) => {
// Step 1: Stop default form reload
event.preventDefault();
// Step 2: Clear old state
setIsSubmitting(true);
setErrors([]);
// Step 3: Client-side validation (custom rules)
const newErrors = [];
if (fieldA < 10) newErrors.push("Value must be at least 10.");
if (fieldB < 1 || fieldB > 100) newErrors.push("Percentage must be between 1 and 100.");
if (!dateField) newErrors.push("Date is required.");
if (newErrors.length > 0) {
setErrors(newErrors);
setIsSubmitting(false);
return; // STOP EARLY โ don't send request
}
// Step 4: Build request
const payload = { fieldA: fieldA, fieldB: fieldB, date_field: dateField };
const request = new Request(apiEndpoint, { method: "POST", headers, body: JSON.stringify(payload) });
try {
// Step 5: Send request
const response = await fetch(request);
// Step 6: Handle server response
if (!response.ok) {
const errorData = await response.json();
setErrors(errorData.message ?? ["Unexpected error"]);
setIsSubmitting(false);
} else {
window.location.reload();
}
} catch (err) {
// Step 7: Handle network failure
setErrors(["Network error"]);
setIsSubmitting(false);
}
};
return (
<form onSubmit={handleSubmit}>
<label>
Value:
<input
type="number"
required
min="10"
value={fieldA}
onChange={(e) => setFieldA(Number(e.target.value))}
/>
<ErrorMessage type={errors.fieldA} />
</label>
<label>
Percentage:
<input
type="number"
required
min="1"
max="100"
value={fieldB}
onChange={(e) => setFieldB(Number(e.target.value))}
/>
<ErrorMessage type={errors.fieldB} />
</label>
<label>
Date:
<input
type="date"
required
value={dateField}
onChange={(e) => setDateField(e.target.value)}
/>
<ErrorMessage type={errors.dateField} />
</label>
<button type="submit">Submit</button>
{/* General errors (like server/network issues) */}
<ErrorMessage type={errors.general} />
</form>
);
สแด๊ฐแดสแดษดแดแด
In JavaScript, value == null
is a pattern commonly used to check if a variable is either null
or undefined
, meaning it has not been set at all. This works due to the behavior of the loose equality (==
) operator:
value == null
Returns true
only if value
is:
null
undefined
It does not match other falsy values like ""
, 0
, NaN
, or false
.
To check for any falsy value, if (!value)
is used instead.
-
undefined
: A variable has been declared but not assigned, or a property/key doesn't exist. -
null
: Indicates an intentional absence of a value, often where an object or meaningful value is expected.
While both mean "no value," null
is deliberate, and undefined
is often implicit.
Use null
to explicitly indicate that a value is:
-
Not yet set, but expected later:
let user = null; // will be assigned after login
-
No longer valid:
dbConnection = null; // released after closing
-
Intentionally empty in data models:
user.middleName = null; // not applicable
-
Fallback value when returning "no result":
return result || null;
Assigning ""
or []
means the value exists but is empty:
let connection = null; // no value
connection = []; // empty array, ready for data
connection = ""; // empty string, still defined
Use null
when you want to show a value is absent on purpose, not just empty.