> For the complete documentation index, see [llms.txt](https://codemash.gitbook.io/codemash/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://codemash.gitbook.io/codemash/cloud/database/schema/field-types/nested-form.md).

# Nested form

An object with its own fields inside the record — an address with street, city and location — up to five levels deep.

A **Nested Form** groups fields into one object inside the record: `address` with `street`, `city` and `geo`. Its fields can be of any type — nested forms and arrays included — up to **five levels** deep. The members are closed: only declared fields may be stored.

**Builder:** Form fields panel → **Nested Form** · widget `nested_form` · data type `object.schema.v1`

## Data schema

```json
{
  "address": {
    "type": "object",
    "properties": {
      "street": { "type": "string", "minLength": 1 },
      "city": { "type": "string" },
      "geo": {
        "type": "object",
        "properties": {
          "type": { "type": "string", "enum": ["Point"] },
          "coordinates": { "type": "array", "items": { "type": "number" } }
        }
      }
    },
    "required": ["street"]
  }
}
```

| Keyword      | Required | What it does                                                                     | Example                              |
| ------------ | -------- | -------------------------------------------------------------------------------- | ------------------------------------ |
| `type`       | yes      | Always `"object"`.                                                               | `"object"`                           |
| `properties` | **yes**  | The child fields: a map of name → field, any of the 17 field types, recursively. | `{ "street": { "type": "string" } }` |
| `required`   | no       | The child names that must be present.                                            | `["street"]`                         |

* The root of a schema is open (`additionalProperties: true`): a record may carry undeclared top-level keys. A nested form is **closed**: an undeclared member is refused.
* Two member shapes are reserved: `value` + `currency` is a [Currency](/codemash/cloud/database/schema/field-types/currency.md) field, `type` + `coordinates` is a [Geolocation](/codemash/cloud/database/schema/field-types/geolocation.md) field. A nested form that declares exactly those members is refused at save time.

### What the server refuses

Every write — insert, update, replace; from Cloud, the API, an SDK, the CLI or an AI agent — is checked against the published schema. A broken value is answered with one error per field: the code, and `Field '<name>' is invalid: <reason>`. Nothing is written.

| Case                                                  | Code                     | The reason it gives                                            |
| ----------------------------------------------------- | ------------------------ | -------------------------------------------------------------- |
| a required field is missing or `null`                 | `CM-ERRORS-DATABASE-030` | `a value is required`                                          |
| the value is not an object                            | `CM-ERRORS-DATABASE-039` | `must be an object (nested form)`                              |
| a required child is missing                           | `CM-ERRORS-DATABASE-030` | on `address.street`: `a value is required`                     |
| an undeclared member                                  | `CM-ERRORS-DATABASE-047` | `unknown member 'zip' (allowed: street, city, geo)`            |
| a child breaks its own rule                           | the child's code         | on `address.geo`: `longitude 200 must be between -180 and 180` |
| a dotted update through a field that is not an object | `CM-ERRORS-DATABASE-039` | `is a string and has no member 'x'`                            |

A keyword that is not in the table above is refused when the schema is **saved**, not when a record is written: `CM-ERRORS-SCHEMA-010` — `Data schema violates meta-schema: Property 'sort' has not been defined and the schema does not allow additional properties. Path 'properties.address.sort' (field 'properties.address' checked as object.schema.v1).`

Also refused at save time:

* more than five levels (root fields are level 1) — `CM-ERRORS-SCHEMA-036` `Schema field 'a.b.c.d.e.f' is nested 6 levels deep; the limit is 5.`
* a `required` name that is not declared — `CM-ERRORS-SCHEMA-038` `Schema field 'address' requires nested fields that it does not declare: zip.`
* the currency or geolocation member shape — `CM-ERRORS-SCHEMA-010` `Field 'properties.address' does not match exactly one field type …`.

## UI schema

The cell uses the `nested_form` widget and lays out its child fields in `children` — one cell per child, with the child's own name as `fieldId`; nested again for deeper levels.

```json
{
  "colSpan": 12,
  "element": {
    "fieldId": "address",
    "meta": {
      "widget": "nested_form",
      "label": "Address",
      "nestedForm": { "collapsible": true, "collapsed": false, "inline": false }
    },
    "children": [
      { "colSpan": 12, "element": { "fieldId": "street", "meta": { "widget": "text", "label": "Street", "required": true } } },
      { "colSpan": 12, "element": { "fieldId": "city", "meta": { "widget": "text", "label": "City" } } },
      { "colSpan": 12, "element": { "fieldId": "geo", "meta": { "widget": "geo_location", "label": "Geo" } } }
    ]
  }
}
```

| Option                   | What it does                                                           |
| ------------------------ | ---------------------------------------------------------------------- |
| `nestedForm.collapsible` | Shows a header that folds the form.                                    |
| `nestedForm.collapsed`   | Whether it starts folded.                                              |
| `nestedForm.inline`      | Renders the child fields in the parent's grid instead of a framed box. |

Every cell also takes the common layout options: `label`, `placeholder`, `help` (the text under the input), `required`, `readonly` and `hidden`. Keys outside this list — `watermark`, `hint`, `permissions` — are refused when the schema is saved (`CM-ERRORS-SCHEMA-012`), see [Schema authoring guide](/codemash/cloud/database/schema.md#one-contract-no-second-dialect).

<figure><img src="https://712692422-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FHy372bqsaex2cqS3ikhL%2Fuploads%2Fgit-blob-2bf1a5af0e01a8fab24cc66a1086f1b2660d471a%2Fnesting-form-1.png?alt=media" alt="Edit Record form of sc_nesting_cloud_orders: Name sc order 1; a framed Address nested form with Street Main St 1, City Vilnius and Geo with Latitude 54.68 and Longitude 25.27; a Phones array with 2 of 1–3 rows, Add row, and the rows +370 600 00001 and +370 600 00002 with move and remove buttons; Order lines below"><figcaption><p>A record form with a nested form (Address) and an array (Phones).</p></figcaption></figure>

## Records

A record the schema accepts:

```json
{ "address": { "street": "Main St 1", "city": "Vilnius", "geo": { "type": "Point", "coordinates": [25.27, 54.68] } } }
```

A record it refuses:

```json
{ "address": { "city": "Vilnius", "zip": "01100" } }
```

The answer: `CM-ERRORS-DATABASE-030 and CM-ERRORS-DATABASE-047` — `Field 'address.street' is invalid: a value is required · Field 'address' is invalid: unknown member 'zip' (allowed: street, city, geo)`. In Cloud the same sentence is shown under the field before the form is sent; the server repeats the check on every API write.

## Good to know

* Update one member with a dotted path: `{ "address.city": "Kaunas" }`; filter and sort on it the same way (`address.city`). Details: [Nested forms and arrays](/codemash/cloud/database/schema/nesting.md).
* The records list shows a nested value as a short summary of its members.
* In the builder, the field editor opens child editors inside the dialog; the depth guard stops at five levels, counted like the server.

## Do it from code

```ts
await norbix.api.database.insertOne({
  collectionName: 'orders',
  document: JSON.stringify({ address: { street: 'Main St 1', city: 'Vilnius' } }),
});
```

```sh
norbix api database one insert --collectionName orders --document '{"address":{"street":"Main St 1","city":"Vilnius"}}'
```

The client setup is on the [Database page](/codemash/cloud/database.md#use-it-from-code). API: [Insert One](/codemash/api-reference/database/collections/insert-one.md) · [Save Database Schema](/codemash/api-reference/database/schemas/save-database-schema.md)

## Related

[Nested forms and arrays](/codemash/cloud/database/schema/nesting.md) · [Array](/codemash/cloud/database/schema/field-types/array.md) · [JSON](/codemash/cloud/database/schema/field-types/json.md) · [Field type reference](/codemash/cloud/database/schema/field-types.md) · [Schema authoring guide](/codemash/cloud/database/schema.md) · [Create a schema](/codemash/cloud/database/collections/create-schema.md)
