Requests & Validation Patterns - humanbit-dev-org/templates GitHub Wiki

Form Validation & Control Flow

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, and step.

  • For example, type="number" prevents entering non-numeric text, and if any required 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."

Client-Side Validation & Submit Flow (Annotated)

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>
);

Language semantics & patterns

ส€แด‡๊œฐแด‡ส€แด‡ษดแด„แด‡

Loose equality and the intentional use of null

Loose Equality Check with == null in JavaScript

What == null Checks

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

Note

It does not match other falsy values like "", 0, NaN, or false.
To check for any falsy value, if (!value) is used instead.


null vs. undefined

  • 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.


When to Use null

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;

Not the Same as Empty Values

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.



โš ๏ธ **GitHub.com Fallback** โš ๏ธ