> How to add and remove entities, fields, relations and enum values through the GraphQL API with upsertEntity

# Schema Changes

The schema is data like any other: entities, fields and relations are records of the `entity` system
entity. You change them with `upsertEntity`, the same way you upsert a record.

```graphql filename="create-entity.graphql"
mutation {
  upsertEntity(input: {
    name: "invoice"
    entity_fields_add: [
      { name: "number", data_type: TEXT, validation_string: "required;unique" }
      { name: "total", data_type: FLOAT, format: { decimals: 2, currency: "EUR" } }
      { name: "status", data_type: ENUM,
        entity_field_enum_values_add: [{ value: "draft" }, { value: "sent" }, { value: "paid" }] }
    ]
    entity_relations_add: [
      { field_name: "customer", type: ManyToOne, target_field_name: "invoices",
        target_entity: { id: "customer-entity-uuid" } }
    ]
  }) {
    data { id }
  }
}
```

Without an `id`, `upsertEntity` creates the entity. With the `id` of an existing entity, it changes
that entity. The generated API serves the new types on the next request.

## Fields

| Key                            | Description                                                                                                                |
| ------------------------------ | -------------------------------------------------------------------------------------------------------------------------- |
| `name`                         | Field name on the generated types                                                                                          |
| `data_type`                    | `TEXT`, `LONGTEXT`, `RICHTEXT`, `INT`, `FLOAT`, `JSON`, `DATE`, `DATETIME`, `TIME`, `BOOLEAN`, `ARRAY`, `ENUM`, `SEQUENCE` |
| `validation_string`            | Rules separated by `;`, see below                                                                                          |
| `default_value`                | Value for records created without one                                                                                      |
| `format`                       | Display options: `decimals`, `grouping`, `currency`, `locale`, `durationStyle`                                             |
| `formula`                      | Makes a `FLOAT` field a read-only computed number                                                                          |
| `entity_field_enum_values_add` | Allowed values of an `ENUM` field                                                                                          |

A `SEQUENCE` field also needs a number range in `entity_sequence_set`.

To change an existing field, pass it in `entity_fields_add` with its `id` and only the keys that
change.

`validation_string` takes `rule:param1,param2` pairs separated by `;`:

| Rule         | The value must                             |
| ------------ | ------------------------------------------ |
| `required`   | not be null or an empty string             |
| `nullable`   | nothing; null and empty string are allowed |
| `unique`     | be unique across all records of the entity |
| `regex:<p>`  | match the pattern `<p>`                    |
| `min:<n>`    | be greater than or equal to `<n>`          |
| `max:<n>`    | be less than or equal to `<n>`             |
| `in:<a>,<b>` | be one of the listed values                |

A formula references fields and relations by path, for example
`round(sum(time_entries.duration) / 3600, 2) * customer.hourly_rate`. The relations it uses must
already exist, so add the formula in a later request than the relations. A plain field cannot become
a formula field or back.

## Several entities in one request

New entities can be created inside a relation's `target_entity`. A new entity needed in more than
one place gets a [placeholder id](/data/mutations#placeholder-ids): the first mention carries its
`name` and creates it, later mentions refer to it.

```graphql filename="create-model.graphql"
mutation {
  upsertEntity(input: {
    name: "invoice"
    entity_relations_add: [
      { field_name: "order", type: ManyToOne, target_field_name: "invoices",
        target_entity: {
          id: "${replace_uuid.order}", name: "order"
          entity_fields_add: [{ name: "number", data_type: TEXT }]
          entity_relations_add: [
            { field_name: "customer", type: ManyToOne, target_field_name: "orders",
              target_entity: { id: "${replace_uuid.customer}", name: "customer" } }
          ]
        } }
      { field_name: "customer", type: ManyToOne, target_field_name: "invoices",
        target_entity: { id: "${replace_uuid.customer}" } }
    ]
  }) {
    data { id }
  }
}
```

The whole request succeeds or fails as one.

## Removing

`entity_fields_remove`, `entity_relations_remove` and `entity_field_enum_values_remove` take the
`id` of what to drop. The data in a removed field or relation is deleted with it. Remove a relation
on the entity that defines it: on the other entity, `entity_relations_remove` changes nothing.

Prefer `_add` and `_remove` over `entity_fields_set` and `entity_relations_set`. A `_set` replaces
the whole list, so every field or relation you do not repeat is removed.

Next: [Data](/data/overview)
