# Field types

> Every field builder of locosdk/schema, its options, its TypeScript type, its SQLite column and its MCP input type.

Fields are declared in `schema.ts` with builders from `locosdk/schema`. The options are PocketBase's field JSON under PocketBase's own names, minus `id`, `name` and `type`. How a schema fits together: [Schema as code](https://golocomotive.fr/docs/developers/schema.md).

## Types at a glance

| builder | TypeScript | SQLite column | MCP input |
|---|---|---|---|
| `text()` | `string` | `TEXT DEFAULT '' NOT NULL` | string |
| `email()`, `url()`, `editor()` | `string` | `TEXT DEFAULT '' NOT NULL` | string |
| `number()` | `number` | `NUMERIC DEFAULT 0 NOT NULL` | number, integer when `onlyInt`, min and max kept |
| `bool()` | `boolean` | `BOOLEAN DEFAULT FALSE NOT NULL` | boolean |
| `date()` | `string` | `TEXT DEFAULT '' NOT NULL` | string `YYYY-MM-DD HH:MM:SS.sssZ` |
| `autodate()` | `string` | `TEXT DEFAULT '' NOT NULL` | never an input |
| `select(values)` | one of `values`, or `''` unless `required` | `TEXT DEFAULT '' NOT NULL` | an enum of the values |
| `select(values, { maxSelect: n })`, n > 1 | an array of the values | `JSON DEFAULT '[]' NOT NULL` | an array of the enum |
| `relation(target)` | `string`, a record id | `TEXT DEFAULT '' NOT NULL` | a record id of the target |
| `relation(target, { maxSelect: n })`, n > 1 | `string[]` | `JSON DEFAULT '[]' NOT NULL` | an array of ids |
| `file()` | `string`, the stored name | `TEXT DEFAULT '' NOT NULL` | never an input: files go through the UI |
| `file({ maxSelect: n })`, n > 1 | `string[]` | `JSON DEFAULT '[]' NOT NULL` | never an input |
| `json<T>()` | `T \| null` | `JSON DEFAULT NULL` | any |
| `password()` | `string`, never output | `TEXT DEFAULT '' NOT NULL` | only `password` and `passwordConfirm` on an auth create |

The id column is `TEXT PRIMARY KEY DEFAULT ('r'||lower(hex(randomblob(7)))) NOT NULL`. Columns are sorted by name when a table is made. Dates are UTC text, `''` when empty.

## Options every field takes

| option | what |
|---|---|
| `required` | a value must be set. On a select it also drops `''` from the type |
| `hidden` | never in a response to anyone but a superuser, never in an MCP input, never readable in a client filter |
| `presentable` | the field names a record: in relation pickers and in the journal's label |
| `help` | a description: the schema form's hint and the MCP tool's field description |

## Per type

| builder | options |
|---|---|
| `text` | `min`, `max` (characters), `pattern` (a regular expression), `autogeneratePattern`, `primaryKey` |
| `email`, `url` | `exceptDomains`, `onlyDomains` |
| `editor` | `maxSize`, `convertURLs` |
| `number` | `min`, `max`, `onlyInt` |
| `date` | `min`, `max` |
| `autodate` | `onCreate`, `onUpdate` (the builder's default is `{ onCreate: true }`) |
| `select(values)` | `maxSelect` |
| `relation(target)` | `maxSelect`, `minSelect`, `cascadeDelete` |
| `file` | `maxSelect`, `maxSize` (bytes, 5 MB when unset), `mimeTypes`, `protected`, `thumbs` |
| `json<T>` | `maxSize` |
| `password` | `min`, `max`, `pattern`, `cost` (bcrypt, 0 means 10) |

`geoPoint` has no builder. A raw `{ type: 'geoPoint' }` spec works, and the filter reaches `geo.lon` and `geo.lat`.

## Relations

- The target is a collection NAME. `defineSchema` resolves it to the collection id and throws on an unknown name.
- `cascadeDelete: true`: deleting the target removes its id from this field, and deletes this record when the field ends empty.
- `required: true` without a cascade: deleting the target is refused when it leaves this field empty.
- Otherwise the target's id is removed from the field. All of it happens in one transaction.

## Indexes

`indexes` is a list of raw `CREATE INDEX` statements, PocketBase's way. A partial unique index works:

```ts
indexes: ["CREATE UNIQUE INDEX idx_clients_code ON clients (code) WHERE code != ''"],
```

A unique index violation answers `validation_not_unique` on its fields. Dropping an index from a table with rows is refused at boot: a migration does it.

## System fields

`base` and `auth` add `id`, `created` and `updated`. `auth` adds `email`, `emailVisibility`, `verified`, `password` and `tokenKey`, and unique indexes on `email` and `tokenKey`. Declaring one of these names in `fields` overrides its options. `autodates` picks which autodates a collection keeps.

## Validation codes

A refused value answers 400 with `data.<field>: { code, message }`, PocketBase's codes: `validation_required`, `validation_min_text_constraint`, `validation_max_text_constraint`, `validation_invalid_format`, `validation_not_unique`, `validation_values_mismatch`, `validation_invalid_old_password` and the others of PocketBase's field validators.
