3 March 2025

Formity / Expry: The Best Way to Store Forms in Databases

Certain types of applications may require storing forms in a database. For instance, a quiz app might let users create and manage their own quizzes, while a social network could allow each group to define a custom onboarding form to collect specific member information.

What is the capital of Australia?

  • The Challenge of Storing Forms

    Building an application like this requires a structured way to represent forms that can be stored in a database. A JSON-based representation is a great approach, but it comes with challenges - most notably, the need for an algorithm to convert JSON into functional JavaScript code.

    The complexity grows as forms become more dynamic. Consider a multi-step form with conditional logic, where questions adapt based on previous answers. To achieve this, you would need to define a JSON structure capable of handling various logical conditions, including comparison, boolean and arithmetic operators. Managing this manually can be both time-consuming and error-prone.

    The Solution: Formity / Expry

    This is where Expry simplifies the process. Expry enables you to define logic using a structured JSON syntax and seamlessly convert it into JavaScript. You can either create custom mappings or leverage built-in operators to handle logic efficiently.

    Even better, Expry provides first-class support for Formity, making it effortless to build advanced multi-step forms with conditional logic - all while keeping the JSON structure clean, maintainable, and free of unnecessary complexity.

    Key Features of Expry

    Expry offers a powerful set of features, making it the go-to tool for creating JSON-based logic.

    Unlimited Possibilities

    Define an unlimited number of JSON-based operators to execute any logic you want. With Expry, the possibilities are endless.

    Built-in Operators

    Expry provides a package with essential operators, including arithmetic, conditional and comparison operators, so you don't have to build them from scratch.

    First-class Support for Formity

    Expry provides a dedicated package with Formity-specific operators. Using this package, you can significantly simplify the creation of multi-step forms.

    How It Works

    Expry consists of multiple packages. The main package offers a way to map JSON to JavaScript code but lacks built-in operators. To define operators, we use the createExpry function, which returns a function for executing JSON-based logic.

    import { createExpry, Executions } from "@expry/system";
    
    type Operations = {
      map: {
        params: { input: unknown; as: unknown; in: unknown };
        return: unknown[];
      };
      add: {
        params: unknown[];
        return: unknown;
      };
    };
    
    const operations: Executions<Operations> = {
      map(args, vars, expry) {
        const array = expry(args.input, vars) as unknown[];
        const as = expry(args.as, vars) as string;
        return array.map((value) => {
          return expry(args.in, { ...vars, [`$${as}`]: value });
        });
      },
      add: (args, vars, expry) => {
        const array = args.map((num) => expry(num, vars)) as number[];
        return array.reduce((acc, val) => acc + val, 0);
      },
    };
    
    const expry = createExpry<[Operations]>(operations);
    
    const expression: unknown = {
      $map: {
        input: [1, 2, 3],
        as: "num",
        in: { $add: ["$$num", 1] },
      },
    };
    
    const result = expry(expression);
    
    console.log(result); // [2, 3, 4]
    

    To use built-in operators, we can include an additional package that provides a wide range of operators, such as arithmetic, comparison, and array operators.

    import { createExpry } from "@expry/system";
    
    import { basicOperations, BasicOperations } from "@expry/basic";
    
    const expry = createExpry<[BasicOperations]>(basicOperations);
    
    const expression: unknown = {
      name: { $concat: ["$name", " ", "$surname"] },
      mostFavoriteSports: {
        $filter: {
          input: "$sports",
          as: "sport",
          cond: { $gt: ["$$sport.rating", 8] },
        },
      },
    };
    
    const variables: Record<string, unknown> = {
      name: "John",
      surname: "Doe",
      sports: [
        { name: "football", rating: 9 },
        { name: "basketball", rating: 10 },
        { name: "tennis", rating: 5 },
        { name: "swimming", rating: 7 },
      ],
    };
    
    const result = expry(expression, variables);
    
    console.log(result);
    

    While Expry is versatile, one common use case is forms. To support this, Expry offers a package with Formity operators that streamline multi-step form creation.

    import { Schema, Return } from "@formity/react";
    
    import { createExpry } from "@expry/system";
    import { basicOperations, BasicOperations } from "@expry/basic";
    import { formityOperations, FormityOperations } from "@expry/formity";
    
    import { componentsOperations, ComponentsOperations } from "./components";
    import { zodOperations, ZodOperations } from "./zod";
    
    type Operations = [
      BasicOperations,
      FormityOperations,
      ComponentsOperations,
      ZodOperations,
    ];
    
    const expry = createExpry<Operations>(
      basicOperations,
      formityOperations,
      componentsOperations,
      zodOperations,
    );
    
    export type Values = [Return<unknown>];
    
    export const schema = expry([
      {
        $schema$form: {
          // ...
        },
      },
      {
        $schema$return: {
          // ...
        },
      },
    ]) as Schema<Values>;
    

    Why Choose Expry?

    Expry simplifies the challenges of working with JSON-based logic, making it particularly useful for applications that require storing forms in databases. With its flexible architecture, Expry enables you to implement dynamic logic effortlessly.