> ## Documentation Index
> Fetch the complete documentation index at: https://twenty-claude-cool-pascal-5ay683.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Objets

> Déclarez de nouveaux types d’enregistrements — des tables personnalisées avec leurs propres champs — à l’aide de defineObject.

Les **objets** personnalisés sont de nouveaux types d’enregistrements que votre application ajoute à un espace de travail — carte postale, facture, abonnement, tout ce qui est spécifique à votre domaine. Chaque objet déclare son schéma (champs, relations, valeurs par défaut) et un identifiant universel stable qui survit aux synchronisations et aux déploiements.

```ts src/objects/post-card.object.ts theme={null}
import { defineObject, FieldType } from 'twenty-sdk/define';

enum PostCardStatus {
  DRAFT = 'DRAFT',
  SENT = 'SENT',
  DELIVERED = 'DELIVERED',
  RETURNED = 'RETURNED',
}

export default defineObject({
  universalIdentifier: '54b589ca-eeed-4950-a176-358418b85c05',
  nameSingular: 'postCard',
  namePlural: 'postCards',
  labelSingular: 'Post Card',
  labelPlural: 'Post Cards',
  description: 'A post card object',
  icon: 'IconMail',
  fields: [
    {
      universalIdentifier: '58a0a314-d7ea-4865-9850-7fb84e72f30b',
      name: 'content',
      type: FieldType.TEXT,
      label: 'Content',
      description: "Postcard's content",
      icon: 'IconAbc',
    },
    {
      universalIdentifier: 'c6aa31f3-da76-4ac6-889f-475e226009ac',
      name: 'recipientName',
      type: FieldType.FULL_NAME,
      label: 'Recipient name',
      icon: 'IconUser',
    },
    {
      universalIdentifier: '95045777-a0ad-49ec-98f9-22f9fc0c8266',
      name: 'recipientAddress',
      type: FieldType.ADDRESS,
      label: 'Recipient address',
      icon: 'IconHome',
    },
    {
      universalIdentifier: '87b675b8-dd8c-4448-b4ca-20e5a2234a1e',
      name: 'status',
      type: FieldType.SELECT,
      label: 'Status',
      icon: 'IconSend',
      defaultValue: `'${PostCardStatus.DRAFT}'`,
      options: [
        { value: PostCardStatus.DRAFT, label: 'Draft', position: 0, color: 'gray' },
        { value: PostCardStatus.SENT, label: 'Sent', position: 1, color: 'orange' },
        { value: PostCardStatus.DELIVERED, label: 'Delivered', position: 2, color: 'green' },
        { value: PostCardStatus.RETURNED, label: 'Returned', position: 3, color: 'orange' },
      ],
    },
    {
      universalIdentifier: 'e06abe72-5b44-4e7f-93be-afc185a3c433',
      name: 'deliveredAt',
      type: FieldType.DATE_TIME,
      label: 'Delivered at',
      icon: 'IconCheck',
      isNullable: true,
      defaultValue: null,
    },
  ],
});
```

## Points clés

* Le `universalIdentifier` doit être unique et stable entre les déploiements.
* Chaque champ nécessite un `name`, un `type`, un `label` et son propre `universalIdentifier` stable.
* Le tableau `fields` est facultatif — vous pouvez définir des objets sans champs personnalisés.
* `openRecordIn` définit où les enregistrements de cet objet s’ouvrent lorsqu’on clique dessus : `ObjectOpenRecordIn.USER_CHOICE` (la valeur par défaut, qui suit la préférence de chaque membre de l’espace de travail dans Settings → Experience), `ObjectOpenRecordIn.SIDE_PANEL` ou `ObjectOpenRecordIn.RECORD_PAGE`. Épinglez-le sur `RECORD_PAGE` pour les enregistrements qui ont besoin d’une page complète pour être utilisables, comme les flux de travail et les tableaux de bord, ou sur `SIDE_PANEL` pour les enregistrements qui n’ont de sens qu’en tant que panneau rapide, comme les événements de calendrier.
* `writability` contrôle qui peut écrire des enregistrements de l’objet en général, avant que les autorisations de rôle ne s’appliquent : `MetadataWritability.OPEN` (la valeur par défaut — les rôles de l’espace de travail décident), `MetadataWritability.APPLICATION` (seules les fonctions logiques propres à votre application peuvent créer, mettre à jour ou supprimer des enregistrements ; utilisez ceci pour des objets de type configuration dont les enregistrements confèrent un comportement, afin que les membres de l’espace de travail ayant un large accès aux enregistrements ne puissent pas les modifier via l’API), ou `MetadataWritability.SYSTEM` (réservé aux données gérées par la plateforme). Les opérations de lecture ne sont pas affectées — ceci est imposé côté serveur, contrairement à `isUIEditable`, qui ne fait que masquer les éléments de l’interface utilisateur. Cela existe aussi au niveau de chaque champ, où cela ne peut être que plus strict que le niveau de l’objet.
* Les champs en ligne définis ici n’ont **pas** besoin d’un `objectUniversalIdentifier` — il est hérité de l’objet parent. Utilisez [`defineField()`](/l/fr/developers/extend/apps/data/extending-objects) pour ajouter des champs aux objets que vous ne possédez pas.
* Vous pouvez générer de nouveaux objets avec `yarn twenty dev:add object`, qui vous guide à travers le nommage, les champs et les relations. Voir [Architecture → Scaffolding entities](/l/fr/developers/extend/apps/getting-started/scaffolding).

<Note>
  **Les champs de base sont ajoutés automatiquement.** Lorsque vous définissez un objet personnalisé, Twenty crée pour vous des champs standard comme `id`, `name`, `createdAt`, `updatedAt`, `createdBy`, `updatedBy` et `deletedAt`. Vous n’avez pas besoin de les déclarer dans votre tableau `fields` — uniquement vos champs personnalisés. Vous pouvez remplacer un champ par défaut en en déclarant un avec le même nom, mais c’est rarement une bonne idée.
</Note>

## Types de champ

L’ensemble complet des valeurs de `FieldType`, exportées depuis `twenty-sdk/define` :

| Catégorie                 | Types                                                                                                                              |
| ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| Texte                     | `TEXT`, `RICH_TEXT`, `ARRAY` (de chaînes), `RAW_JSON`                                                                              |
| Numérique                 | `NUMBER` (`universalSettings.dataType` : `'float'` / `'int'` / `'bigint'`), `NUMERIC` (précision arbitraire), `RATING`, `POSITION` |
| Dates                     | `DATE`, `DATE_TIME`                                                                                                                |
| Choix                     | `BOOLEAN`, `SELECT`, `MULTI_SELECT`                                                                                                |
| Composés                  | `FULL_NAME`, `ADDRESS`, `EMAILS`, `PHONES`, `LINKS`, `CURRENCY`, `ACTOR`, `FILES`                                                  |
| Identifiants et relations | `UUID`, `RELATION`, `MORPH_RELATION` (voir [Relations](/l/fr/developers/extend/apps/data/relations))                               |
| Système                   | `TS_VECTOR` (vecteur de recherche en texte intégral, géré par le serveur)                                                          |

Les types composés stockent plusieurs sous-champs (par exemple `FULL_NAME` = prénom + nom de famille ; `CURRENCY` = `amountMicros` + `currencyCode`). `SELECT` et `MULTI_SELECT` nécessitent un tableau `options` comme dans l’exemple ci-dessus.

## Valeurs par défaut

Les valeurs par défaut de type chaîne littérale doivent être entourées de guillemets simples **à l’intérieur** de la chaîne — `defaultValue: "'Draft'"`, et non `defaultValue: "Draft"`. C’est pourquoi le champ `status` ci-dessus utilise `` `'${PostCardStatus.DRAFT}'` ``.

Les chaînes sans guillemets sont réservées aux valeurs par défaut calculées, évaluées lors de la création d’un enregistrement :

* `'uuid'` — génère un UUID (pour les champs `UUID`)
* `'now'` — l’horodatage actuel (pour les champs `DATE_TIME`)

La même convention s’applique aux sous-champs de type chaîne des valeurs par défaut composites (par exemple `{ source: "'MANUAL'" }` sur un champ `ACTOR`) ainsi qu’aux valeurs `SELECT`/`MULTI_SELECT`. Une valeur par défaut littérale de type chaîne laissée sans guillemets génère un avertissement lors de la compilation de votre application.

## Nullabilité

`isNullable` détermine si un champ accepte `NULL`. Sa valeur par défaut est `true` — omettez-le pour les champs facultatifs. Définissez `isNullable: false` pour rendre un champ obligatoire au niveau de la base de données.

Les modifications de `isNullable` sont appliquées à chaque synchronisation, y compris celles qui mettent à jour un champ existant — vous pouvez donc modifier la nullabilité d’un champ en éditant le manifeste puis en relançant la synchronisation.

<Note>
  **Rendre un champ existant non nullable nécessite une valeur par défaut.** Lorsque vous modifiez un champ avec `isNullable: false`, vous devez également fournir une `defaultValue` non nulle. La valeur par défaut remplit rétroactivement toutes les lignes `NULL` existantes avant que la contrainte `NOT NULL` ne soit appliquée ; sans cela, la synchronisation échoue avec `Default value cannot be null for non-nullable fields`. Les champs de relation et les champs `TS_VECTOR` sont toujours nullables, donc `isNullable` n’a aucun effet sur eux.
</Note>

```ts theme={null}
{
  universalIdentifier: 'b1a7c0de-1234-4f00-9abc-000000000000',
  name: 'reference',
  type: FieldType.TEXT,
  label: 'Reference',
  isNullable: false,
  defaultValue: "'N/A'",
}
```

## Et après

* **Connectez cet objet aux autres** — voir [Relations](/l/fr/developers/extend/apps/data/relations) pour le modèle de relation bidirectionnelle.
* **Ajoutez des champs aux objets d’autres applications** — voir [Extending Objects](/l/fr/developers/extend/apps/data/extending-objects) pour `defineField()`.
* **Afficher cet objet dans l’interface utilisateur** — voir [Éléments du menu de navigation](/l/fr/developers/extend/apps/layout/navigation-menu-items) pour ajouter une entrée dans la barre latérale ; voir [Vues](/l/fr/developers/extend/apps/layout/views) pour ajouter des configurations de liste personnalisées.
