> ## Documentation Index
> Fetch the complete documentation index at: https://vlyai-1c28d863.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Input validation

> Catch bad data at the boundary. Convex schema validators, business-rule validation, sanitization.

The first line of defense against bad data, exploits, and bugs.

## Three layers

<Steps>
  <Step title="Schema validators (Convex)">
    `v.string()`, `v.number()`, `v.union(...)` — enforced at the database level. Bad shapes are rejected before your handler runs.
  </Step>

  <Step title="Business rules (in mutations)">
    "Email must be lowercase", "Quantity must be ≥ 1", "Future date for due-by". Throw with a clear message.
  </Step>

  <Step title="Sanitization (for user-provided HTML / markdown)">
    Use [DOMPurify](https://github.com/cure53/DOMPurify) or a markdown renderer with safe defaults. Don't render `dangerouslySetInnerHTML` on raw user input.
  </Step>
</Steps>

## In a Convex mutation

```typescript theme={null}
export const createPost = mutation({
  args: {
    title:   v.string(),
    body:    v.string(),
    teamId:  v.id("teams"),
  },
  handler: async (ctx, { title, body, teamId }) => {
    // Schema validators ran already.
    if (title.length < 1 || title.length > 200) {
      throw new ConvexError({ code: "invalid_title", message: "Title must be 1–200 chars." });
    }
    if (body.length > 100_000) {
      throw new ConvexError({ code: "body_too_long", message: "Body too long." });
    }
    // ... business logic
  },
});
```

## Client-side validation

Use [react-hook-form](https://react-hook-form.com) + [zod](https://zod.dev) for client-side. The same `zod` schema can be used to validate before submission and to type the form.

But: **server-side validation is the truth**. Client-side is for UX.

## Sanitization for rendered content

If users paste HTML or markdown that will be rendered:

```typescript theme={null}
import DOMPurify from "dompurify";
const safe = DOMPurify.sanitize(userMarkdown);
```

For markdown specifically, [react-markdown](https://github.com/remarkjs/react-markdown) with default plugins is safe — it doesn't render raw HTML by default.

## Related

<CardGroup cols={3}>
  <Card title="Convex schema" icon="database" href="/convex/schema">
    Validators reference.
  </Card>

  <Card title="Error handling" icon="alert-triangle" href="/architecture/error-handling">
    What to do when validation fails.
  </Card>

  <Card title="XSS / CSRF" icon="shield-x" href="/security/xss-csrf">
    Defending against rendered attacks.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.