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.
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.