Apportez votre schéma

La page précédente est additive sur l'hôte : votre routing, vos pages, votre rendu et vos autres routes serveur ne bougent pas. Elle ne l'est pas sur le domaine. L'entité est la seule chose que Fougere doit posséder, parce que c'est la seule dont tout le reste dérive.

Adopter Fougere a donc exactement un coût, et cette page est ce coût : vous déclarez votre modèle dans son vocabulaire. La suite explique comment le garder petit, borné, et payé une entité à la fois.

Une entité à la fois

On ne convertit pas un domaine. On convertit une entité.

migrate() est additif : il émet createTable, addColumn, addConstraint et createIndex, rien d'autre. Il ne supprime jamais une colonne, ne réécrit jamais un type, et ne propose rien pour une table qu'aucune entité ne déclare. Une entité Fougere crée donc sa table à côté de celles que gère votre storage actuel, dans la même base, et vos lignes existantes sont hors de sa portée.

Le cas à surveiller est la collision de noms : pour une entité dont la table existe déjà, c'est un addColumn sur votre table qui est proposé, pas une table neuve. Renommez l'une des deux, ou confiez la table volontairement.

La correspondance

PrismaFougere
Stringtext() — avec { min, max } quand vous avez une règle
String?optional(text()), ou nullable(text()) si la colonne est vraiment nullable
Int, Floatnumber()
Booleanbool()
DateTimedate()
Jsonjson()
String @id @default(cuid())primary()
@uniqueunique(champ)
@@unique([listId, docId])unique: [['listId', 'docId']], dans le 2ᵉ argument d'entity()
@indexindexed(champ)
une clé étrangère + @relationref(Author)
l'autre côté de cette relationmany(Post) — un rôle, pas de colonne
@default(now())created()
@updatedAtupdated()
une enum + @default("draft")oneOf('draft', 'published', { default: 'draft' })
@db.VarChar(160)text({ max: 160 })

La dernière ligne, c'est toute la thèse. @db.VarChar(160) nomme un dialecte ; text({ max: 160 }) nomme la règle, et le dialecte en dérive — avec le CHECK, le JSON Schema, le type GraphQL et le maxlength du formulaire.

Ce qui n'a pas d'équivalent

Un modèle Prisma dit ce que la colonne est. C'est un axe sur quatre. Les trois autres n'ont aucune colonne d'où partir, et c'est pour ça que la conversion n'est pas mécanique :

  • lifecycle — qui écrit la valeur, et quand. created() n'est pas @default(now()) sous un plus joli nom : il énonce aussi que la valeur ne peut pas être réécrite à l'update. readOnly(oneOf(…)) énonce que le client ne la fournit jamais.
  • boundary — qui a le droit de la voir. writeOnly() pour le mot de passe qui entre et ne ressort pas. readOnly() pour le champ que le serveur estampille.
  • role — quel rôle il joue. primary, ref, many, unique — lus par le DDL pour les clés et les contraintes, et par la carte pour les relations.

Ces trois règles, vous les appliquez déjà. Elles vivent dans une méthode de service, un DTO, une clause select:, un décorateur @Exclude(), un commentaire de revue. Ce sont de vraies règles sans domicile unique.

La conversion n'est pas mécanique parce que vous écrivez, pour la première fois, des règles que vous appliquiez déjà.

C'est le travail, et c'est aussi le retour : de cette déclaration, le validateur, la table, le type GraphQL, la route REST et le contrat de formulaire sont tous des projections — voir Entités pour les quatre axes en entier.

Ce qui ne bouge pas

  • Votre storage. Fougere ne le revendique pas. Dans les routes que vous n'avez pas converties, continuez d'interroger exactement comme aujourd'hui.
  • Les tables non converties. Rien au boot n'exige que toute la base soit déclarée. Le scan trouve ce qui est dans entities/, et se tait sur le reste.
  • Les requêtes brutes sur une entité convertie. storage.client est le passe-droit du port storage — l'instance Kysely sous-jacente pour l'adapter SQL, cantonnée aux données de cette entité. C'est le seul chemin sur lequel le juge ne siège pas ; voir Le port Storage.

Suite : La CLI — composer un workspace, héberger une Frond, appeler une opération.

Construit avec Fougere — ce site tourne sur le framework qu'il documente.