Example: Custom Server-side Validation

In this example we create a custom REST endpoint (to enforce unique usernames) and we register it with CruddyForms. CruddyForms provides vanilla javascript that calls our endpoint to perform the validation and display any validation errors.
    1
  • Define the schema using Typebox

    Notice the endpoint attribute for the username field.

        // ~/examples/custom/schema.ts
    import {Type} from "@sinclair/typebox";
    import type {FormInputText} from "cruddy-forms";
    
    export const UsernameSchema = Type.Object( {
      username: Type.RegExp(
        /^\S*$/, // No whitespace
          {
            element: "input",
            endpoint: "/api/validation/username?stuff=abc&username=",
            hint: "Username may not contain spaces",
            inputType: "text",
            maxLength: 8,
            placeholder: "your username",
          } satisfies FormInputText,
        ),
    } );
    
    const EmailSchema = Type.Object( {
      email: Type.RegExp(
        /.+@example\.com/i,
          {
            element: "input",
            hint: "Must end in @example.com",
            inputType: "email",
            placeholder: "[email protected]",
          } satisfies FormInputText,
        ),
    } );
    
    const PASS_MIN = 6;
    const PasswordSchema = Type.Object( {
      password: Type.String( {
        element: "input",
        hint: `Use ${PASS_MIN} or more characters`,
        inputType: "password",
        minLength: PASS_MIN,
        placeholder: "your password",
        } satisfies FormInputText ),
    } );
    
    export const Schema = Type.Composite( [
      UsernameSchema, EmailSchema, PasswordSchema,
    ] );
      
    ts
  • 2
  • Implement the validation function, returning a response in the format that CruddyForms expects.

        // ~examples/custom/validation/username.ts
    function response(message: string, status: number) {
      return new Response(JSON.stringify({message}), {status});
    }
    export const usersDatabase = ["pele", "messi"];
    export function validateUsername(params: URLSearchParams) {
      console.log(params);
      const username = params.get("username");
      if (!username) {
        return response("Error: username required", 400);
      }
      if (usersDatabase.includes(username.toLowerCase())) {
        return response("Sorry, username taken", 422);
      }
      return response("", 200); 
    }
      
    ts
    3
  • Expose the validation function as a REST endpoint

    The path to your endpoint should match the one defined in the schema in Step 1. Implementation of the endpoint will depend on your server-side rendering framework. Your endpoint just needs to pass the SearchParams from the incoming request to the validation function from Step 2, and return its response.
  • 4
  • Generate the Form HTML

    Be sure to call form.getHTMLComponent() to get the HTML which will work automatically with the javascript provided by CruddyForms.

        // ~/examples/custom/form.ts
    import {Form} from "cruddy-forms";
    import {Schema} from "~/examples/custom/schema.ts";
    
    export function getHTMLComponent(data?: FormData) {
      const form = new Form(Schema);
      if (data) {
        form.validate(data);
      }
      return form.getHTMLComponent();
    }
      
    ts
  • 5
  • Serve the Form HTML, CSS, and Javascript

    The details of this step will depend on your chosen server-side rendering framework. Your server response should include the generated Form HTML from Step 4, the normform.css file, and the cruddy-client.js script that ships with CruddyForms.
  • 6
  • Resulting Form (Try it!)

    In the Username field, try entering one of the names from the usersDatabase that we defined in Step 2, e.g. pele. Once you move on to the next field, you should see a validation error message.

    Username may not contain spaces
    Use 6 or more characters