Validating Incoming Data
Introduction: Why Validate Incoming Data?
In the previous lesson, you learned how to build consistent API responses so that every request returned a predictable structure. In this lesson, we’ll take the next important step toward building a reliable backend — validating incoming data.
When your API receives data from clients (for example, creating or updating a user), you can’t assume that data is valid or complete. Someone might send:
- an empty name,
- an invalid email address,
- or even omit required fields entirely.
If your backend doesn’t validate data before using it, it could save bad information to memory or a database, crash unexpectedly, or behave in unsafe ways.
Validation acts as a gatekeeper, checking every incoming request and ensuring only clean, well-structured data makes it into your system.
By the end of this lesson, you’ll know how to:
- Use a centralized validation module (
app/lib/validation.ts), - Integrate it into your API routes,
- And observe validation behavior directly in the preview UI.
Understanding the Purpose of Validation
Data validation means verifying that incoming information matches your expectations before you use it.
In your API, this applies to any operation where users send data — especially POST, PUT, and PATCH requests that create or update users.
Validation answers these questions:
- Is this field required?
- Is the value the correct type (
string,number,boolean)? - Does it match allowed values (like
"admin"or"user"for a role)? - Does it have the right format (like an email address)?
For example:
- A name should not be empty.
- An email should look like
"person@example.com". - The role should be one of a specific set of roles.
- The
isActiveflag should be a boolean, not a string like"yes".
By centralizing this logic in one file, you ensure all routes follow the same rules and produce clear error messages that both developers and users can understand.
The Validation Module: app/lib/validation.ts
Your validation logic lives in a single, reusable module that defines rules for checking user input. Let’s look at the core of this file.
What This Function Does
This function is the core validator for all user data operations in your backend.
-
Checks that the body is a plain object.
If someone sends an array, a string, or an invalid JSON body, the validator immediately returns an error message. -
Enforces required fields when needed.
When creating or replacing a user (POST or PUT), required fields such asnameandemailmust be present.
Optional fields likeroleandisActiveare validated only if provided.
When partially updating (PATCH), at least one valid field must be included. -
Validates individual fields.
name: must be a non-empty string.email: must be a string and match a simple email regex pattern.role: optional, but if provided, must be"user","admin", or"moderator".isActive: if provided, must be a boolean (trueorfalse).
-
Returns an array of messages.
Each message describes exactly what’s wrong with the input.
This array is then passed to yourerr()helper, which joins them into a readable string for the frontend.
The result is a single function that can be reused in every route that processes user data.
Integrating Validation into API Routes
Validation becomes part of your route logic in app/routes/api.users.tsx and app/routes/api.users.$id.tsx.
Example: POST /api/users in api.users.tsx
How This Works
- The route first parses the JSON body.
- It calls
validateUserPayload()to check the data. - If any validation fails, the function returns an array of errors.
- The
err()helper receives that array and formats it into a single readable error message before sending it back to the client.
For example, sending this invalid body:
will produce this consistent backend response:
The frontend can now display these messages clearly without guessing the error shape.
How the Validation Appears in the Preview UI
Your Remix UI is designed to visualize backend behavior rather than prevent it.
When you interact with the interface:
- The input fields (Name, Email, Role, etc.) accept any value, even invalid ones.
- The red text area above the JSON preview shows validation or server error messages returned by the backend.
- The
<pre>block below it shows the complete JSON response, even when errors occur.
This design is intentional. It lets you:
- Observe how backend validation works directly.
- Compare raw JSON responses with user-facing error messages.
- Experiment with edge cases like empty inputs, invalid emails, or missing fields.
For example:
Submitting an empty Name and Email will show a red alert reading:
"'name' must be a non-empty string.; 'email' must be a non-empty string."
The <pre> block will display the full backend response object, showing the standardized error structure.
By separating frontend display and backend validation, you ensure that the backend remains the single source of truth for input validation.
Why the Validation Files Are Needed
validation.ts
- Defines all backend validation logic.
- Keeps validation rules centralized.
- Prevents duplication across routes.
- Makes the API safer, since only valid data is processed.
responses.ts
- Ensures every validation error follows a consistent response format.
- Converts arrays of validation messages into a single readable message.
- Guarantees that every API response — success or failure — shares the same structure.
Frontend (_index.tsx)
- Displays validation messages without performing them locally.
- The UI’s job is to show what the backend says, not decide what’s valid.
Together, these files demonstrate a real-world pattern: backend validation, unified responses, and a client that faithfully reports what the server returns.
Summary
In this lesson, you learned:
- How to validate incoming data with the
validateUserPayload()function. - Why validation should happen on the backend instead of the frontend.
- How your Remix UI displays backend validation errors clearly.
- How consistent responses and validation make your API more predictable and professional.
By moving validation into a single backend module, you created a foundation for stronger, safer APIs.
This is exactly how professional teams handle input validation in scalable systems.
Next, you’ll learn how to extend this pattern with structured logging and more robust error handling to make your backend easier to monitor and debug.
