Skip to content

Schema validation

Description

JSON Schema is a vocabulary for annotating and validating data in JavaScript. More about JSON Schema on json-schema.org

A schema can be used from the simplest description of the data type of a value:

{
type: "string"
}
"Foo"

To an object with both rules for required fields and validation rules for single values:

{
type: "object",
properties: {
txt: { type: 'string', minLength: 5 },
num: { type: 'number', maximum: 100 },
},
required: ['txt']
}
{
"txt": "abcde",
"num": 123
}

Using schema with DataContext

Since a DataContext (used in isolation or through the use of Form.Handler) supports JSON Schema, these two examples will result in the same validation for the user:

<Form.Handler data={user}>
<Field.String path="/name" label="Name" minLength={3} required />
<Field.Email path="/email" label="E-mail" required />
<Field.Number
path="/birthyear"
label="Birth year"
minimum={1900}
maximum={2023}
required
/>
</Form.Handler>
const schema = {
type: 'object',
properties: {
name: { type: 'string', minLength: 3 },
email: { type: 'string' },
birthyear: { type: 'number', minimum: 1900, maximum: 2023 }
},
required: ['name', 'email', 'birthyear'],
}
<Form.Handler data={user} schema={schema}>
<Field.String path="/name" label="Name" />
<Field.Email path="/email" label="E-mail" />
<Field.Number path="/birthyear" label="Birth year" />
</Form.Handler>

This makes it possible to create a uniform, testable description and requirements specification for the data, which can be tested independently of frontend code, and used across systems, e.g. frontend and backend.

Complex schemas

In addition to basic validation as in the example above, JSON Schema can be used for more complex. Examples of definitions supported by the standard are:

  • Requirement that the object must not have other properties than those defined in properties.
  • Nested data structures and combinations of objects and arrays with rules for array elements (fixed or repetitive elements).
  • Regular expressions for the syntax of individual values.
  • Enum (a set of valid values).
  • Rules for the number of elements in arrays.
  • Rules for the number of properties in objects.
  • Predefined format rules (eg 'uri', 'email' and 'hostname').
  • Logical operators such as 'not', 'oneOf', 'allOf' and 'anyOf' which can be filled with rules for all or part of the data set.
  • Rule set based on the content of values (if-then-else).
  • Rules (sub-schemas) that become applicable if a given value is present.
  • Reuse within the definition, such as one and the same object structure being used as a definition for several locations in a structure.

To learn more about what is possible with the JSON Schema standard, see json-schema.org.

Demos

Schema for single field

Code Editor
<Field.String
  schema={{
    type: 'string',
    minLength: 5,
  }}
/>

Schema for a whole data set

Company information

Code Editor
<Form.Handler
  data={{
    address: 'Prefilled address',
  }}
  schema={{
    type: 'object',
    properties: {
      name: {
        type: 'string',
        minLength: 2,
      },
      address: {
        type: 'string',
        minLength: 3,
      },
    },
    required: ['name', 'address'],
  }}
>
  <Card spacing="small" bottom="small">
    <Form.MainHeading>Company information</Form.MainHeading>

    <Field.String path="/name" label="Name" />
    <Field.String path="/address" label="Address" />
  </Card>

  <Form.SubmitButton />
</Form.Handler>

Schema with if-rule

Customer information

Code Editor
<Form.Handler
  data={{}}
  schema={{
    type: 'object',
    properties: {
      name: {
        type: 'string',
      },
      customerType: {
        type: 'string',
        enum: ['corporate', 'private'],
      },
      companyName: {
        type: 'string',
      },
    },
    if: {
      properties: {
        customerType: {
          enum: ['corporate'],
        },
      },
      required: ['customerType'],
    },
    then: {
      required: ['name', 'companyName'],
    },
    else: {
      required: ['name'],
    },
  }}
>
  <Card spacing="small" bottom="small">
    <Form.MainHeading>Customer information</Form.MainHeading>

    <Field.String path="/name" label="Name" />
    <Field.String
      path="/customerType"
      label="Customer type (corporate or private)"
    />
    <Field.String
      path="/companyName"
      label="Company name (required for corporate customers)"
    />
  </Card>

  <Form.SubmitButton />
</Form.Handler>

Dependant list schema

Becoming a new customer, this form requires at least one normal account to be added, unless the customer opens a BSU account, then normal accounts can still be added, but is optional.

Customer information

Accounts

Standard accounts

BSU Account

Code Editor
<Form.Handler
  data={{
    accounts: [{}],
  }}
  schema={{
    type: 'object',
    definitions: {
      account: {
        type: 'object',
        properties: {
          accountNumber: {
            type: 'string',
            pattern: '^[0-9]{11}$',
          },
          alias: {
            type: 'string',
            minLength: 2,
            maxLength: 32,
          },
        },
        required: ['accountNumber'],
      },
    },
    properties: {
      name: {
        type: 'string',
      },
      email: {
        type: 'string',
      },
      phone: {
        type: 'string',
      },
      accounts: {
        type: 'array',
        items: {
          $ref: '#/definitions/account',
        },
      },
      bsuAccount: {
        $ref: '#/definitions/account',
      },
    },
    oneOf: [
      {
        properties: {
          accounts: {
            type: 'array',
            minItems: 1,
          },
        },
      },
      {
        properties: {
          accounts: {
            type: 'array',
            minItems: 0,
          },
          bsuAccount: {
            type: 'object',
            required: ['accountNumber'],
          },
        },
        required: ['bsuAccount'],
      },
    ],
  }}
>
  <Flex.Vertical spacing="small">
    <Form.MainHeading>Customer information</Form.MainHeading>
    <Card spacing="small">
      <Field.String path="/name" label="Name" />
      <Field.Email path="/email" label="E-mail" />
      <Field.PhoneNumber path="/phone" label="Phone number" />
    </Card>

    <Form.MainHeading>Accounts</Form.MainHeading>
    <Card spacing="small">
      <Form.SubHeading>Standard accounts</Form.SubHeading>

      <Iterate.Array path="/accounts">
        <Flex.Horizontal align="flex-end">
          <Field.BankAccountNumber
            elementPath="/accountNumber"
            label="Account number"
          />
          <Field.String
            elementPath="/alias"
            label="Alias"
            width="medium"
          />
          <Iterate.ArrayRemoveElementButton icon={TrashIcon} />
        </Flex.Horizontal>
      </Iterate.Array>

      <Iterate.ArrayPushButton
        icon="add"
        icon_position="left"
        text="Add account"
        path="/accounts"
        pushValue={{}}
        size="medium"
      />

      <Form.SubHeading>BSU Account</Form.SubHeading>
      <Field.BankAccountNumber
        path="/bsuAccount/accountNumber"
        label="Account number"
      />
      <Field.String path="/bsuAccount/alias" label="Alias" />
    </Card>

    <Form.SubmitButton />
  </Flex.Vertical>
</Form.Handler>