ScaleRocket/Web

Input Validation

Validation patterns with Zod schemas, Edge Function parameter validation, and file upload checks.

Overview

Input validation is critical for security and data integrity. ScaleRocket uses Zod for schema-based validation on both client and server, with additional checks for Edge Functions and file uploads.

Zod Schema Validation

Zod is the recommended validation library. Define schemas and validate data before processing:

Basic Schema

import { z } from "zod";

const userSchema = z.object({
  email: z.string().email("Invalid email address"),
  password: z.string().min(8, "Password must be at least 8 characters"),
  name: z.string().min(1, "Name is required").max(100),
});

type UserInput = z.infer<typeof userSchema>;

Validating Input

const result = userSchema.safeParse(formData);

if (!result.success) {
  // result.error.flatten() gives a structured error object
  const errors = result.error.flatten().fieldErrors;
  // { email: ["Invalid email address"], password: ["..."] }
  return;
}

// result.data is fully typed and safe to use
const { email, password, name } = result.data;

Common Schema Patterns

// Optional field with default
const settingsSchema = z.object({
  notifications: z.boolean().default(true),
  language: z.enum(["en", "fr", "de"]).default("en"),
});

// String transformations
const slugSchema = z.string().trim().toLowerCase().regex(/^[a-z0-9-]+$/);

// Number constraints
const paginationSchema = z.object({
  page: z.coerce.number().int().positive().default(1),
  limit: z.coerce.number().int().min(1).max(100).default(20),
});

// Union types
const idSchema = z.union([z.string().uuid(), z.string().cuid()]);

Client-Side Form Validation

Validate before submitting to prevent unnecessary API calls:

const handleSubmit = (e: FormEvent) => {
  e.preventDefault();
  const result = userSchema.safeParse({ email, password, name });

  if (!result.success) {
    const errors = result.error.flatten().fieldErrors;
    setErrors(errors);
    return;
  }

  // Submit validated data
  submitForm(result.data);
};

Edge Function Parameter Validation

Always validate incoming request bodies in Edge Functions:

import { z } from "zod";

const requestSchema = z.object({
  userId: z.string().uuid(),
  action: z.enum(["activate", "deactivate"]),
  reason: z.string().max(500).optional(),
});

Deno.serve(async (req) => {
  let body;
  try {
    body = await req.json();
  } catch {
    return new Response(JSON.stringify({ error: "Invalid JSON" }), { status: 400 });
  }

  const result = requestSchema.safeParse(body);
  if (!result.success) {
    return new Response(
      JSON.stringify({ error: "Validation failed", details: result.error.flatten().fieldErrors }),
      { status: 400 }
    );
  }

  // Use result.data safely
  const { userId, action } = result.data;
});

File Upload Validation

For admin media uploads and user file attachments, validate before processing:

const MAX_FILE_SIZE = 5 * 1024 * 1024; // 5MB
const ALLOWED_TYPES = ["image/jpeg", "image/png", "image/webp", "application/pdf"];

function validateFile(file: File): string | null {
  if (file.size > MAX_FILE_SIZE) {
    return `File too large. Maximum size is ${MAX_FILE_SIZE / 1024 / 1024}MB.`;
  }
  if (!ALLOWED_TYPES.includes(file.type)) {
    return `Invalid file type. Allowed: ${ALLOWED_TYPES.join(", ")}`;
  }
  return null; // valid
}

With Zod

const fileSchema = z.object({
  name: z.string(),
  size: z.number().max(5 * 1024 * 1024, "File exceeds 5MB limit"),
  type: z.enum(["image/jpeg", "image/png", "image/webp", "application/pdf"], {
    errorMap: () => ({ message: "Unsupported file type" }),
  }),
});

Validation Checklist

  • Validate all form inputs before submission (client-side)
  • Validate all Edge Function request bodies (server-side)
  • Use safeParse instead of parse to avoid throwing exceptions
  • Set maximum lengths on string fields to prevent payload abuse
  • Validate file size and MIME type before upload
  • Never trust client-side validation alone; always re-validate on the server

Tips

  • Share Zod schemas between client and server by placing them in a shared package.
  • Use z.coerce for query parameters and form data that arrive as strings.
  • Combine Zod with React Hook Form using @hookform/resolvers/zod for integrated form validation.
  • Sanitize HTML content (e.g., blog posts) with a library like dompurify after Zod validation.

Done reading? Mark this page as complete.

On this page