A field refers to one specific attribute of each record stored for a model. Like columns in traditional databases, a field records a characteristic or property about the model's subject matter. Fields define the overall structure of the model's storage, deciding the names, datatypes, and validations on all the data a model stores.
For example, take a model representing each customer of an ecommerce system. The customer model captures the information related to each person who bought something, such as their name and date of birth. In Gadget, each attribute becomes a field, which then stores a value for each record. For our example customer model, we might create these fields:
name (field type: string ) - stores the customer's full name.
dateOfBirth (field type: date / time ) - stores the customer's birth date.
You can think of each field as a column in a spreadsheet, and each record as a row.
Field defaults
In Gadget, you can set a default value for most field types in the schema editor. This default value will be used when creating a record if no specific value is provided. You can modify the default value anytime, but the change will only be enforced creating for new records. Default values will not be applied when updating existing records, even if the field with the default is required.
Types of Fields
Field Type
Description
Validations
Configurations
id
Stores a unique, sequential identifier for each record in a model. IDs are unique within the model, but not across models. IDs may not be added as a new field to a model.
No applicable validations
None
string
Stores an arbitrary length string with UTF-8 encoding.
Required, Uniqueness, String Length Range, Run Code Snippet, RegExp Pattern
None
number
Stores numerical values, including integers and floats.
Required, Uniqueness, Number Min/Max Range, Run Code Snippet
Decimal
enum
Stores one string or a list of strings picked from a global list of allowed values. Can also be configured to accept any string as a valid input. Similar to an enum in traditional SQL databases.
Required, Uniqueness, Run Code Snippet
Enum options, Allow selection of multiple options, Allow any string to be selected as an option
rich text
Stores an enhanced-Markdown formatted string. Can produce formatted HTML for the markdown.
Required, Uniqueness, String Length Range, Run Code Snippet, RegExp Pattern
Formatting Options
boolean
Stores a true or false value.
Required, Uniqueness, Run Code Snippet
True/False
date / time
Stores a date or date and time value with a timezone. Accepts and serves data as an ISO-8601 string. Fields configured to not include time will ignore the time
Required, Uniqueness, Run Code Snippet, RegExp Pattern,
Date Format
email
Stores an email as a string.
Required, Uniqueness, String Length Range, Run Code Snippet, RegExp Pattern
No Default
url
Stores a URL as a string.
Required, Uniqueness, String Length Range, Run Code Snippet, RegExp Pattern
No Default
file
Stores a file like an image or PDF, , in cloud storage. See storing files for more information.
Required, File Size Range, Image File, Run Code Snippet
Option for Private Access
color
Stores a hexadecimal representation of a color.
Required, Run Code Snippet, Color
Color Picker UI
vector
Stores a vector of floats. Useful for storing embeddings and performing vector operations like cosine similarity.
Required, Uniqueness, Vector Dimensions, Run Code Snippet
None
json
Stores any JSON value. Any valid JSON is accepted.
Required, Uniqueness, Run Code Snippet
JSON Indexing
password
Stores a bcrypt hashed password. One way encrypts the value, so the unencrypted text can't later be retrieved. Suitable for storing passwords and other secrets you might need to compare against but not reveal.
Strong Password
None
encrypted string
Stores a string encrypted at rest and and decrypted when accessed for extra security. Suitable for storing secrets whose contents you need later.
New fields can be added to new or existing models by navigating to the relevant schema, and clicking the plus button shown below. From here, you may configure the API identifier, type or relationship, set default values, and specify any configurations or validations.
Working with a record state field
The shopifyShop model includes a record state field that stores a specific status for each record. While using record state for implementing business logic is optional, it can be a useful way to determine permissions and actions.
The record state field holds the current state of each record. The field configuration panel defines the available states for this field.
Default system fields
Each model in Gadget has three fields managed by default. These fields are:
id: This field captures a unique ID for each of your records within a table
Gadget will automatically handle ID assignments. To ensure performance and stability, Gadget auto-increments IDs on every new mutation
regardless of the status of the mutation. As a result, your saved records will always have a unique ID, but the IDs are not guaranteed to
be sequential. As an example, your IDs may increment from 11 to 13, as the mutation to create the 12th record failed.
createdAt: This field captures the timestamp at record creation
updatedAt: This field captures the timestamp of the last event which changed the record
System fields cannot be given a different name / API identifier.
Computed fields
Produces a value according to a Gelly code snippet using other field values.
Extending fields with validations and configurations
In Gadget, validations and configurations are ways to ensure your data conforms to a required standard.
Validations are an extension of the field component of a model that helps prevent the entry of malformed or inconsistent data into your system. When modifying records, Gadget verifies that any new data satisfies the validations defined for each field. If an action attempts to write invalid data, it will be unsuccessful, and the invalid data will not be saved.
When making API calls to write data, Gadget provides structured error messages that indicate what data is invalid. These error messages can be presented directly to users, helping guide them in correcting the problematic data. However, you may choose to provide your own custom error messages for validation.
Validations play a crucial role in maintaining data integrity and ensuring that only valid and coherent data is stored in your system. They serve as a safeguard against incorrect or incompatible data and facilitate a smooth user experience by providing clear feedback on data entry errors.
Configurations, on the other hand, control the behavior of fields when entering data into a model. These can be used to specify default values or other parameters of a field to further maintain data integrity and user privacy.
Applicable validations to field types
To apply a validation to a field, you can select any items from the validation drop down shown below. Gadget offers a selection of common validations for each field type. These vary from one field type to another, and are described in detail below.
Required validation
A required validation refers to the process of ensuring that a particular input or data field is not empty or null when it is required to have a value. It is a form of data validation that checks whether a user has provided the necessary information before proceeding with a certain operation or action.
Uniqueness validation
The Uniqueness validation in Gadget ensures that each value for a field occurs only once in the database. It is commonly used for fields like email or username to guarantee that each value is unique within the system.
Underlying database constraints are leveraged to enforce the uniqueness validation reliably, even in transactional contexts and under load.
If a Uniqueness validation is added to a model that already contains data, the setup may fail if there are duplicate values for the field. In such cases, the uniqueness constraint is not enforced until the duplicate data is corrected by either deleting records or modifying the field values to ensure uniqueness. Once the data is made unique, the validation can be re-added and verified to succeed.
The Uniqueness validation can be configured as case-sensitive or case-insensitive by toggling the Case Sensitive option. When the option is disabled, values are considered duplicates regardless of letter casing. This is useful for scenarios like URL slugs or emails, where users may enter values with different casing but uniqueness should be maintained regardless of casing.
Number Min/Max range validation
Number min/max range validation ensures whether a numeric value falls within a specified range of minimum and maximum values. It is commonly used to ensure that numeric input or data meets certain constraints or requirements.
String length range validation
String length range validation is a type of data validation that ensures whether a string has a specific length or falls within a certain range of lengths. It is commonly used to enforce length restrictions or requirements on user input or data fields
Image validation
The Image File validation ensures that uploaded files meet three criteria:
They have an image based MIME type or subtype — see MDN's reference.
They are in one of the following image file formats: gif, jpg, png, svg, or webp (common web formats); apng, bmp, bpg, cr2, cur, dcm, flif, heic, ico, jp2, jpm, jpx, jxr, psd, or tif (additional formats).
They do not contain an animation if the Allow animated images option is disabled.
File size range validation
File size range validation ensures the size of a file falls within a specified range of acceptable sizes. It is commonly used when handling file uploads or processing files in applications. By validating the file size, you can control resource consumption, prevent server overload, and maintain data integrity.
Color Validation
Color validation ensures whether a given value represents a valid color. It is typically used to ensure that color values provided by users or within a system adhere to a specific format or range of valid colors.
Vector dimension validation
The Vector dimensions validation in Gadget ensures that all vectors stored in a field have the same count of dimensions.
It's important to note that Gadget restricts the sorting or filtering of vectors unless all the stored vectors and input vectors have the same dimension count. This is because many vector operations used in Gadget are not defined for vectors with different dimensions.
To enforce the consistency of vector dimensions, you can add a Vector dimensions validation to a field. This validation ensures that all vectors stored in the field have the same number of dimensions.
For more details about this validation and any related errors, you can refer to the GGT_DIFFERENT_VECTOR_DIMENSIONS error in the Gadget documentation.
RegEx pattern validation
RegExp also known as regular expression pattern validation, is used to validate strings or data against a specific pattern or format using regular expressions. Regular expressions (RegEx) are sequences of characters that define a search pattern, allowing for powerful and flexible pattern matching and validation.
Gadget uses the RegExp engine built into node.js to run RegExp Pattern validations. For information on how to write JavaScript regular expressions, please refer to MDN's reference.
Adding custom code validation
In Gadget, you can add your custom validations written in JavaScript. The custom validation runs a JS function each time a record is created or updated to check if that change should succeed. If the function reports no errors, then the save will be complete, and if the data is somehow invalid, the validation can add structured error messages to the different fields of the record being changed.
Each custom validation file must export a function as the default export function from the module. The function can be marked async or return a Promise. The function shouldn't return anything and, instead, should add errors to any fields determined to be invalid using the passed-in errors object, which holds a structured set of errors keyed by a field.
Validation functions get passed one big context object and not individual arguments. This is because there are a lot of arguments
you may or may not need. Most Gadget users use JavaScript destructuring to pull out just the context keys that they need. The most common
keys used from the context are the record itself at the record key, the api object, which is an already set up instance of the
JavaScript client for the application at api, and the Errors object at the errors key.
Here's a custom validation that tests if a record's full name is not null and its last name is also not null:
JavaScript
import type { FooBarFieldValidationContext } from "gadget-server";
export default async ({
api,
record,
errors,
logger,
field,
}: FooBarFieldValidationContext) => {
if (record.firstName && !record.lastName) {
errors.add("lastName", "must be set if the first name is set");
}
};
import type { FooBarFieldValidationContext } from "gadget-server";
export default async ({
api,
record,
errors,
logger,
field,
}: FooBarFieldValidationContext) => {
if (record.firstName && !record.lastName) {
errors.add("lastName", "must be set if the first name is set");
}
};
Here's a validation that tests if a record's phone number is valid:
JavaScript
import PhoneNumber from "awesome-phonenumber";
import type { FooBarFieldValidationContext } from "gadget-server";
export default async ({
api,
record,
errors,
logger,
field,
}: FooBarFieldValidationContext) => {
const number = new PhoneNumber(record["customerPhoneNumber"]);
if (!number.isValid()) {
errors.add("customerPhoneNumber", "is not a valid phone number");
}
};
import PhoneNumber from "awesome-phonenumber";
import type { FooBarFieldValidationContext } from "gadget-server";
export default async ({
api,
record,
errors,
logger,
field,
}: FooBarFieldValidationContext) => {
const number = new PhoneNumber(record["customerPhoneNumber"]);
if (!number.isValid()) {
errors.add("customerPhoneNumber", "is not a valid phone number");
}
};
If a validation function throws an error while executing, the action validating the current record will fail, and if the action is transactional, any operations performed in the run function will be rolled back.
Strong Password
Strong passwords enforces users to select passwords based on the following criteria:
Passwords must be at least 8 characters
Passwords must contain at least 1 number and 1 special character
Applicable configurations to field types
Gadget offers a selection of common configurations for field types, which vary from one field to another. As with validations, these can be specified by selecting the field of interest in your model's schema. Configurations usually appear as checkboxes or as ranges you can specify.
Default value
Default values may be specified to ensure that any empty values will not be null. This configuration may be applied to most data types, with a few exceptions.
These can be leveraged to populate required fields without relying on the user. However, default values may not be paired with the unique validation constraint.
Decimals
The decimals configuration is used to enforce the maximum number of decimal places a number may have in a model.
However, it doesn’t enforce formatting or rounding—numbers will be stored exactly as entered. If a value exceeds the allowed decimal places, Gadget will return an error instead of rounding or adding placeholder zeros.
Enum options
Enums require options, which are the values a field can take when creating a new record. For example, we could create a User enum, and have options for admin and user to specify user types.
By default, no options are provided, which actually means the only valid entry is an empty string.
Allow selection of multiple options
By default, enums only accept one option per record. However, this option allows for multiple selection.
Here is an example on how to select multiple enum options:
This option will allow for any string to be considered a valid option for an enum field. This essentially eliminates the need to specify what are valid options, although you can still specify them.
This configuration can be useful when you want to allow users to extend the allowed enums. For example, consider you are building a file sharing system for school subjects, which uses enums as subject tags. Instead of restricting users to specific subjects, such as math or music, you could use this configuration to allow for new subject options.
Include time
By default, Gadget includes time in the date/time field type. However, this can be toggled via this option, allowing for just a date to be specified in a record.
If you specify that time should not be included in this field, but the user enters a date with a time, the time will be removed by Gadget before entering the table. However, if you specify time should be included, and the user fails to specify a time, Gagdet will not allow this record to be saved.
Only allow file access by users who can access the files record
file fields in Gadget can be marked as private or not. When the field is private, unauthenticated users aren't able to
access the URL for a file at all. For authenticated users, Gadget generates securely signed URLs to files that expire after 3 hours. This is
ideal because it means if a user logs out or their access is revoked, they can no longer access the file after the 3 hour window has
elapsed. Private files are not CDN cached which is often desirable if they can contain any kind of sensitive data.
If you want anyone to be able to access files, like the images on a blog, or profile photos on a social network, you can mark the field as not private. In this instance, Gadget will generate URLs that never expire so users are always able to access the file, and the files will be cached on Gadget's CDN for maximum performance.
If you change a file field's private configuration, existing uploaded files are not re-permissioned. If you want to make all the files stored by a field private, you would need to re-upload each one to recreate them with the new access permissions.