# How to Add Custom Validation Rules to FckSignups: A Complete Guide for Front-End and Back-End

> Learn to add custom validation rules to FckSignups using Zod schemas on both front-end and back-end. This guide ensures data integrity and security for your signups.

- Repository: [Abdullah/FckSignups](https://github.com/BraveOPotato/FckSignups)
- Tags: how-to-guide
- Published: 2026-09-07

---

**Add custom validation rules to FckSignups by implementing Zod schemas in both the React front-end ([`src/hooks/useModal/fieldsMaker.tsx`](https://github.com/BraveOPotato/FckSignups/blob/main/src/hooks/useModal/fieldsMaker.tsx)) and the Cloudflare Worker back-end ([`cloudflare-worker/urlHandlers/handleSubmitTool.ts`](https://github.com/BraveOPotato/FckSignups/blob/main/cloudflare-worker/urlHandlers/handleSubmitTool.ts)) to ensure data integrity and security.**

FckSignups is an open-source tool submission platform that currently performs minimal validation on user-provided data. Whether you need to enforce minimum character lengths, restrict special characters, validate URL formats, or run asynchronous checks, this guide walks you through adding robust custom validation rules to both layers of the application.

## Understanding FckSignups' Current Validation Architecture

Before adding custom rules, you need to understand where FckSignups currently handles data:

- **Front-end (React)**: The `useModal` hook in [`src/hooks/useModal/fieldsMaker.tsx`](https://github.com/BraveOPotato/FckSignups/blob/main/src/hooks/useModal/fieldsMaker.tsx) builds the "Add a Tool" modal and defines fields sent to the back-end. It only checks for empty values before submission.

- **Back-end (Cloudflare Worker)**: The [`cloudflare-worker/urlHandlers/handleSubmitTool.ts`](https://github.com/BraveOPotato/FckSignups/blob/main/cloudflare-worker/urlHandlers/handleSubmitTool.ts) handler receives JSON payloads, performs basic sanity checks for required keys, and forwards data to the GitHub API.

Neither layer currently implements schema-based validation, leaving the system vulnerable to malformed submissions.

## Step 1: Install a Validation Library

**Zod** is the recommended choice for FckSignups because it runs in both browser and worker environments with zero dependencies.

```bash
npm install zod

```

Add this to your [`package.json`](https://github.com/BraveOPotato/FckSignups/blob/main/package.json) and commit the lockfile changes.

## Step 2: Define Your Custom Validation Schema

Create a dedicated validation file that centralizes all rules for tool submissions.

```typescript
// src/validation/toolSchema.ts
import { z } from "zod";

export const toolSchema = z.object({
  name: z
    .string()
    .min(3, "Tool name must be at least 3 characters")
    .max(50, "Tool name cannot exceed 50 characters")
    .regex(/^[a-zA-Z0-9\s-]+$/, "Name may only contain letters, numbers, spaces and dashes"),
  url: z
    .string()
    .url("Please provide a valid URL")
    .max(200, "URL is too long"),
  description: z
    .string()
    .min(10, "Description must be at least 10 characters")
    .max(300, "Description cannot exceed 300 characters"),
});

```

This schema demonstrates several **custom validation rules**: length constraints, pattern matching with regex, and URL format verification. Extend this file for any additional fields your form collects.

## Step 3: Apply Validation in the Front-End

Import your schema into [`src/hooks/useModal/fieldsMaker.tsx`](https://github.com/BraveOPotato/FckSignups/blob/main/src/hooks/useModal/fieldsMaker.tsx) and validate before transmitting data.

```tsx
import { toolSchema } from "../../validation/toolSchema";
import { z } from "zod";

// Inside your submit handler
const handleSubmit = async () => {
  try {
    // Throws ZodError if validation fails
    const validData = toolSchema.parse(formState);
    await sendToWorker(validData);
  } catch (e) {
    if (e instanceof z.ZodError) {
      const fieldErrors = e.format();
      setErrors(fieldErrors);
      return; // Prevent submission
    }
    throw e; // Re-throw unexpected errors
  }
};

```

Display errors in your modal UI:

```tsx
{errors.name && <p className="error">{errors.name._errors[0]}</p>}
{errors.url && <p className="error">{errors.url._errors[0]}</p>}

```

## Step 4: Guard the Back-End Endpoint

Even with front-end validation, **always validate server-side**. Malicious clients can bypass your UI entirely.

In [`cloudflare-worker/urlHandlers/handleSubmitTool.ts`](https://github.com/BraveOPotato/FckSignups/blob/main/cloudflare-worker/urlHandlers/handleSubmitTool.ts):

```typescript
import { toolSchema } from "../../../src/validation/toolSchema";
import { z } from "zod";

export async function handleSubmitTool(request: Request) {
  const body = await request.json();

  try {
    const valid = toolSchema.parse(body);
    // Existing logic: push to GitHub
    return new Response(JSON.stringify({ success: true }), { status: 200 });
  } catch (err) {
    if (err instanceof z.ZodError) {
      return new Response(
        JSON.stringify({ 
          error: err.errors.map(e => e.message),
          fields: err.issues.map(i => i.path[0])
        }),
        { status: 400, headers: { "Content-Type": "application/json" } }
      );
    }
    return new Response(
      JSON.stringify({ error: "Invalid payload" }),
      { status: 400, headers: { "Content-Type": "application/json" } }
    );
  }
}

```

This ensures that even crafted requests reaching your Cloudflare Worker are rejected before touching the GitHub API.

## Step 5: Return Structured Error Responses

Both layers should return consistent error shapes:

| Property | Purpose |
|----------|---------|
| `error` | Human-readable message or array of messages |
| `field` | Which input failed validation (for UI highlighting) |

Example worker response for multiple failures:

```json
{
  "error": ["Tool name must be at least 3 characters", "Please provide a valid URL"],
  "fields": ["name", "url"]
}

```

## Step 6: Add Unit and Integration Tests

Create [`src/validation/toolSchema.test.ts`](https://github.com/BraveOPotato/FckSignups/blob/main/src/validation/toolSchema.test.ts) to verify your rules:

```typescript
import { toolSchema } from "./toolSchema";
import { describe, it, expect } from "vitest";

describe("toolSchema", () => {
  it("rejects names shorter than 3 characters", () => {
    expect(() => 
      toolSchema.parse({ name: "Ab", url: "https://example.com", description: "Valid description here" })
    ).toThrow();
  });

  it("rejects URLs without protocol", () => {
    expect(() =>
      toolSchema.parse({ name: "ValidTool", url: "example.com", description: "Valid description" })
    ).toThrow("Please provide a valid URL");
  });

  it("accepts valid complete submissions", () => {
    expect(() =>
      toolSchema.parse({
        name: "MyTool",
        url: "https://example.com/tool",
        description: "A helpful description"
      })
    ).not.toThrow();
  });
});

```

Add integration tests for the worker endpoint to verify end-to-end behavior.

## Advanced Custom Validation Patterns

### Async Validation (URL Reachability)

Extend your schema with refinements that fetch URLs:

```typescript
const toolSchemaWithReachability = toolSchema.extend({
  url: z.string().url().max(200).refine(
    async (val) => {
      try {
        const res = await fetch(val, { method: "HEAD", mode: "no-cors" });
        return true;
      } catch {
        return false;
      }
    },
    { message: "URL appears unreachable" }
  ),
});

```

**Note**: Use async validation only in the back-end or with loading states in the UI to prevent blocking interactions.

### Conditional Validation

Apply rules based on other field values:

```typescript
const conditionalSchema = toolSchema.extend({
  category: z.enum(["free", "paid"]),
  price: z.number().optional().refine(
    (val, ctx) => {
      if (ctx.parent.category === "paid" && (val == null || val <= 0)) {
        ctx.addIssue({
          code: z.ZodIssueCode.custom,
          message: "Paid tools require a valid price",
        });
      }
      return true;
    }
  ),
});

```

## Key Files Reference

| File | Purpose |
|------|---------|
| [`src/hooks/useModal/fieldsMaker.tsx`](https://github.com/BraveOPotato/FckSignups/blob/main/src/hooks/useModal/fieldsMaker.tsx) | React modal building and form submission |
| [`cloudflare-worker/urlHandlers/handleSubmitTool.ts`](https://github.com/BraveOPotato/FckSignups/blob/main/cloudflare-worker/urlHandlers/handleSubmitTool.ts) | Cloudflare Worker request handler |
| [`src/validation/toolSchema.ts`](https://github.com/BraveOPotato/FckSignups/blob/main/src/validation/toolSchema.ts) | Central Zod schema (create this) |
| [`src/utils/formatters.ts`](https://github.com/BraveOPotato/FckSignups/blob/main/src/utils/formatters.ts) | Utility helpers for error formatting |

## Summary

- **Dual-layer validation is mandatory**: Front-end for UX, back-end for security
- **Use Zod** for isomorphic validation across browser and worker runtimes
- **Centralize schemas** in `src/validation/` to share between front-end and back-end
- **Always parse with `schema.parse()`** in [`handleSubmitTool.ts`](https://github.com/BraveOPotato/FckSignups/blob/main/handleSubmitTool.ts) before processing submissions
- **Return structured errors** with field names to enable precise UI feedback
- **Test your schemas** independently from UI components for maintainability

## Frequently Asked Questions

### What validation library works best with FckSignups?

**Zod** is optimal because it has zero runtime dependencies, excellent TypeScript inference, and runs identically in browsers and Cloudflare Workers. Alternatives like Yup or Joi require polyfills or have larger bundle sizes.

### Can I skip front-end validation if I validate in the worker?

No. Front-end validation provides immediate feedback that improves user experience. Back-end validation alone creates frustrating cycles where users submit forms, wait for network round-trips, then correct errors. Implement both layers as demonstrated in [`fieldsMaker.tsx`](https://github.com/BraveOPotato/FckSignups/blob/main/fieldsMaker.tsx) and [`handleSubmitTool.ts`](https://github.com/BraveOPotato/FckSignups/blob/main/handleSubmitTool.ts).

### How do I validate file uploads or images?

FckSignups' current architecture stores tools as GitHub repository data, not blob storage. For image validation, add URL validation to check that provided image URLs return valid content-type headers, or integrate a service like Cloudflare Images and validate upload tokens in your worker.

### Why does my schema fail in the worker but work in the browser?

Check that your import path in [`cloudflare-worker/urlHandlers/handleSubmitTool.ts`](https://github.com/BraveOPotato/FckSignups/blob/main/cloudflare-worker/urlHandlers/handleSubmitTool.ts) correctly resolves to [`../../../src/validation/toolSchema.ts`](https://github.com/BraveOPotato/FckSignups/blob/main/../../../src/validation/toolSchema.ts). The Cloudflare Worker build may need path aliases configured, or you can duplicate the schema into the `cloudflare-worker/` directory if module resolution becomes problematic.