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.