Dashboard

How to Add Form Validation to an AI-Built App

How to add form validation to an AI-built app: browser checks for fast feedback, server checks for safety, and one shared schema so the two never drift.

Steve Jefferson
Steve Jefferson
Developer Advocate
12 October 20261 min read

To add form validation to an AI-built app, you need two layers that do different jobs: checks in the browser that tell people what to fix, and checks on the server that refuse bad data no matter where it came from. AI builders are good at the first layer and often skip the second, because the browser layer is what you see working in the preview. The app looks protected while the door behind it is open.

Why browser validation alone is not validation

Browser checks run on the user's device, which means the user controls them. Anyone can turn off scripts, edit the page, or send a request directly to your server without ever loading the form. MDN's guide to form validation warns that client-side validation is too easy to bypass, so malicious users can still send bad data to your server, and says apps should validate form-submitted data on the server as well. Browser checks are a usability feature.

Server validation is the security feature. OWASP's input validation cheat sheet lists client-only checks as a common pitfall: validate on the server even when the browser checks the same fields, and use client-side checks for immediate feedback. Treat every request that reaches your server as untrusted until your server has checked it.

Add form validation once, then run it twice

The most reliable way to keep both layers honest is to define the rules once and run them in both places. If the rules live in two files, they drift. A schema library such as Zod lets one definition serve the browser and the server.

ts
// schema.ts, shared by the form and the API route
import { z } from "zod";

export const signupSchema = z.object({
  name: z.string().trim().min(1, "Enter your name").max(80),
  email: z.string().trim().toLowerCase().email("Enter a valid email"),
  quantity: z.coerce.number().int().min(1).max(50),
  notes: z.string().max(1000).optional(),
});

In the browser, run signupSchema.safeParse(formValues) and show the messages next to each field. On the server, run the same call against the request body before you touch the database:

ts
// api route
const result = signupSchema.safeParse(await request.json());
if (!result.success) {
  return Response.json({ errors: result.error.flatten().fieldErrors }, { status: 400 });
}
const data = result.data; // typed, trimmed, bounded

Two habits matter here. Use the parsed result.data, never the raw body, so trimming and limits apply. And return a 400 with field errors, so the form can display them.

What AI-built forms usually leave unchecked

Read your generated form against this list. Each row is a gap that shows up when the agent builds for the happy path.

Input

Missing check

What to add

Email

Format only checked in the browser

Server-side format check, normalize case

Free text

No length limit

A maximum on every string

Numbers

Accepts negatives or huge values

Integer, minimum, and maximum

Dropdowns

Server trusts the submitted value

Check it against the allowed list

File uploads

Type and size trusted from the browser

Check type and size on the server

Hidden fields

Prices or IDs sent by the client

Look them up server-side instead

Dates

Past or impossible dates allowed

Range check

Repeat submits

Double-click creates two records

Disable the button and make the endpoint safe to repeat

The hidden-field row deserves attention. If a form sends a price from the browser and the server believes it, a user can change it. The server should compute the price from the product ID itself. Forms that edit existing records have a related risk when two people save at once, covered in two people editing the same record.

A prompt that gets the server layer built

Left alone, an agent tends to validate where you can see it. Say what you want explicitly:

text
Add validation to the signup form. Define one Zod schema in a shared file.
Use it in the form for inline error messages AND in the API route, which must
return a 400 with field errors if parsing fails. The route must use the parsed
data, not the raw request body. Add limits on every string and number.
Do not rely on HTML required or pattern attributes alone.

The last sentence is there because HTML attributes are easy for an agent to add and easy to mistake for finished work.

Prove the server says no

Do not trust the form to test the server, since the form is the part that blocks bad input. Send the bad request yourself:

bash
curl -i -X POST https://your-app.example/api/signup \
  -H "Content-Type: application/json" \
  -d '{"name":"","email":"not-an-email","quantity":9999}'

You should get a 400 and a list of field errors. If you get a 200, the server is not validating, whatever the form does. Add this request as an automated test so a later change cannot quietly remove the check. The post on testing an AI-built app before launch covers where such tests fit.

Error messages people can use

Say what to do, not what went wrong in the code. "Enter a valid email" beats "Invalid input". Keep messages next to the field, keep the user's other answers in place, and never clear the form on error. For a complete worked form, the guide to adding a contact form to an AI-built app puts these pieces together, and email verification is the next step when a valid-looking address is not enough.

Validation also helps against injection, though it is not the whole defense. See SQL injection in AI-generated code for why queries still need to be parameterized. For the full picture of putting an app together, start from how to build an app with AI.

Frequently asked questions

Do I need both client-side and server-side validation?

Yes. The client side gives quick feedback to honest users. The server side protects your data from everyone, including people who bypass the form.

What is the best way to validate forms in React?

Use a schema library such as Zod with your form library of choice, and reuse the same schema on the server. The exact form library matters less than having one source of truth for the rules.

Is HTML required attribute enough?

No. It is a convenience for the browser and can be removed or bypassed. It never replaces a server check.

How do I validate an email address properly?

Check the format on both sides, then confirm ownership by sending a message to it. Format checks cannot tell you whether the address exists or belongs to the person submitting it.

How did this land?

About the author

Steve Jefferson
Steve Jefferson

Developer Advocate

Steve builds something with Swarmz every week and writes up what worked, what broke, and what he'd do differently. Tutorials and hands-on guides are his lane.

Share

Get the next post in your inbox

One email a month. Product updates, engineering posts, and the best of Built with Swarmz.

I agree to receive emails about AI building tips and Swarmz product news. Unsubscribe any time.