# Collection field types

Every field type a collection version takes, what each stores, its settings and the errors a write can get.

Each field on a [collection version](/collections/overview) has a `type`. The
type decides what a record stores under the field's `slug`, which settings
the field takes, and what a write is refused for.

```json title="A version's fields"
{
  "fields": [
    {
      "slug": "name",
      "title": "Name",
      "type": "text",
      "required": true,
      "max": 80
    },
    {
      "slug": "size",
      "title": "Size",
      "type": "select",
      "options": [
        { "value": "s", "label": "Small" },
        { "value": "m", "label": "Medium" }
      ]
    },
    {
      "slug": "seats",
      "title": "Seats",
      "type": "integer",
      "min": 1,
      "max": 500
    },
    { "slug": "budget", "title": "Budget", "type": "money", "currency": "usd" },
    { "slug": "office", "title": "Office", "type": "location" }
  ]
}
```

A record written against that version:

```json title="Record data"
{
  "name": "Acme",
  "size": "m",
  "seats": 40,
  "budget": { "amount": 250000, "currency": "usd" },
  "office": { "address": { "city": "Berlin", "country": "DE" } }
}
```

> **Note:**
  Add fields in the BuildBase console under **Collections**, on a draft version.
  The console shows each type by its label (the second column below); the API
  takes the stored name.

## The types

| `type`         | Console label   | A record stores                                   | Settings                            |
| -------------- | --------------- | ------------------------------------------------- | ----------------------------------- |
| `text`         | Text            | A string                                          | `min`, `max`, `pattern`             |
| `rich-text`    | Rich text       | A string of formatted text                        | `min`, `max`                        |
| `email`        | Email           | A string in email form                            | `min`, `max`, `pattern`             |
| `link`         | Link            | An `http` or `https` URL                          | `min`, `max`, `pattern`             |
| `color`        | Color           | A string                                          | `min`, `max`, `pattern`             |
| `number`       | Number          | A number, decimals allowed                        | `min`, `max`                        |
| `integer`      | Whole number    | A whole number                                    | `min`, `max`                        |
| `bool`         | Checkbox        | `true` or `false`                                 | None                                |
| `date`         | Date            | A date string                                     | None                                |
| `select`       | Choice          | One option's `value`                              | `options`                           |
| `multi-select` | Multiple choice | A list of option values                           | `options`, `min`, `max`             |
| `money`        | Money           | `{ amount, currency }`, the amount in minor units | `currency` (required), `min`, `max` |
| `location`     | Location        | `{ address?, point? }`                            | None                                |
| `file`         | File            | An asset reference, or a list of them             | `maxMb`, `maxFiles`                 |

Note the name `bool`, not `boolean`. A type the version does not know is
refused when the version is saved.

## What every field has

| Property       | Type      | Required | Description                                                                     |
| -------------- | --------- | -------- | ------------------------------------------------------------------------------- |
| `slug`         | `string`  | Yes      | The key the value is stored under. Starts with a letter; `a-z`, `0-9`, `-`, `_` |
| `title`        | `string`  | Yes      | The field's name, shown on forms and in the console                             |
| `type`         | `string`  | Yes      | One of the types above                                                          |
| `helpText`     | `string`  | No       | A hint shown under the field                                                    |
| `required`     | `boolean` | No       | A write without a value is refused. Default `false`                             |
| `defaultValue` | the type  | No       | Must itself be a valid value for the field                                      |

The console offers a default value for text, rich text, number, checkbox,
date, color, link and email fields. A choice, a whole number, money, a
location and a file start empty.

## Limits

The platform's ceilings, whatever a field says:

| Limit                                 | Value                |
| ------------------------------------- | -------------------- |
| Fields in one version                 | 100                  |
| `slug` length                         | 64                   |
| `title` length                        | 120                  |
| `helpText` length                     | 500                  |
| Options on a choice field             | 200                  |
| One option's `value` or `label`       | 120                  |
| `pattern` length                      | 200                  |
| `max` on a field that has a `pattern` | 1,000                |
| One text value                        | 1,000,000 characters |
| A location's address line             | 200 characters       |

## Text, email, link and color

`min` and `max` count characters. An `email` value must look like an email
address, and a `link` value must start with `http://` or `https://`.

`pattern` is a regular expression the **whole** value must match, on `text`,
`email`, `link` and `color` fields. Two rules keep a pattern safe to run on
what strangers send:

- A field with a `pattern` sets a `max` of 1,000 or less.
- A pattern that could take very long to test is refused when the version is
  saved: no backreference (`\1`), and no repeated group that itself repeats or
  holds a choice (`(a+)+`, `(a|aa)*`). `[A-Z]{3}-\d+`, `(foo|bar)` and `(ab)+`
  are fine.

```json title="A postcode field"
{
  "slug": "postcode",
  "title": "Postcode",
  "type": "text",
  "max": 10,
  "pattern": "[0-9]{5}"
}
```

## Number and whole number

A string that reads as a number is stored as one: `"5"` is stored as `5`.
`integer` refuses anything that is not a whole number. `min` and `max` bound
the value, and may be negative.

## Checkbox and date

A `bool` field takes `true` or `false`, or the strings `"true"` and
`"false"`, and stores the boolean. A `date` field takes a date string
(`"2026-11-03"` or a full ISO timestamp) and stores it as sent; a bare number
is refused.

## Choice and multiple choice

Both need `options`: 1 to 200 of them, each with a distinct `value` and a
`label`. The record stores the `value`; the label is for people.

A `multi-select` value is a list of distinct option values. Its `min` and
`max` count how many are picked.

```json title="A multiple-choice field and its value"
{
  "slug": "tags",
  "title": "Tags",
  "type": "multi-select",
  "max": 3,
  "options": [
    { "value": "new", "label": "New" },
    { "value": "sale", "label": "On sale" }
  ]
}
```

```json title="Record data"
{ "tags": ["new", "sale"] }
```

## Money

A money value is `{ "amount": 1999, "currency": "usd" }`: the amount in the
currency's minor units (cents for US dollars, whole yen for yen), never
negative, and a lower case ISO 4217 code for a currency in circulation.

A collection's money field **must** name its `currency`; saving a version with
a money field and no currency is refused. A value in any other currency is
refused, so every record of the field is in the same currency. `min` and `max`
bound the amount, in minor units.

## Location

A location is an address, a map point, or both:

```json title="Record data"
{
  "office": {
    "address": {
      "line1": "Unter den Linden 1",
      "city": "Berlin",
      "postalCode": "10117",
      "country": "DE"
    },
    "point": { "lat": 52.517, "lng": 13.389 }
  }
}
```

The address takes any of `line1`, `line2`, `city`, `state`, `postalCode` and
`country`, each a string of up to 200 characters. `country` is an ISO 3166
two-letter code and is stored upper case. `point` takes exactly `lat` (-90 to 90) and `lng` (-180 to 180). Any other key is refused.

## File

A `file` field holds images as your [assets](/assets/overview), never the
bytes. Its settings, what a record stores and how to write one are on
[the overview](/collections/overview#file-fields).

## When a write is refused

A record written through the API is checked against its version's fields.
The first problem answers `400` with the field and what is wrong:

```json title="400 response"
{ "error": true, "message": "Seats must be at most 500" }
```

A [public form](/forms/overview) submission answers every problem at once:

```json title="400 response"
{
  "success": false,
  "message": "Validation failed",
  "errors": ["Seats must be at most 500", "Budget must be in USD"]
}
```

Keys that are not fields of the version are dropped, not refused. An empty
value (`""`, `null` or `[]`) on an edit clears the field.

## Check values in your own app

The SDK runs the same checks, so a form can say what is wrong before it
sends. The server checks again, and its answer counts.

```typescript title="lib/check-order.ts"
import { checkFieldValues, type FieldDefinition } from '@buildbase/sdk';

export function checkOrder(
  fields: FieldDefinition[],
  data: Record<string, unknown>
) {
  const { values, errors } = checkFieldValues(fields, data);
  // errors: [{ slug: 'seats', code: 'too_large', message: '...' }]
  return errors.length ? { errors } : { values };
}
```

Each error has a `code` your app can put in its own words:

| `code`             | Meaning                                                |
| ------------------ | ------------------------------------------------------ |
| `required`         | A required field has no value                          |
| `invalid_type`     | Not a value of the field's type                        |
| `too_short`        | Fewer characters than `min`                            |
| `too_long`         | More characters than `max`                             |
| `too_small`        | Below `min`                                            |
| `too_large`        | Above `max`                                            |
| `too_few`          | Fewer choices than `min`                               |
| `too_many`         | More choices than `max`, or more files than `maxFiles` |
| `pattern_mismatch` | Does not match the field's `pattern`                   |
| `not_an_option`    | Not one of the field's options                         |
| `duplicate`        | The same choice or file twice                          |
| `wrong_currency`   | Money in another currency than the field's             |

## Next Steps

- [Collections overview](/collections/overview) - versions, file fields and endpoints.
- [Records API](/collections/records-api) - read and delete stored records.
- [Forms](/forms/overview) - collect records from a public endpoint.
