Qu'est-ce que Fougere

Fougere est un framework TypeScript construit autour de deux idées.

Single-schema. Une classe d'entité déclare vos données une fois — et juge elle-même ses entrées : le même validate() tourne dans le navigateur et à la façade. Le juge est lui-même une projection — dérivée de l'axe shape — mais une projection normative, embarquée avec la classe : toutes les autres doivent lui obéir, et elle ne peut pas dériver seule. Les tables SQLite, les types GraphQL, les contrats de formulaire et les surfaces d'API sont des projections de cette déclaration — rien n'est écrit deux fois.

import { entity, primary, text, created, oneOf, date, readOnly, optional } from '@fougere/schema';

export default class Post extends entity({
  id: primary(),
  title: text({ min: 1, max: 160 }),
  body: optional(text()),
  createdAt: created(),
  status: readOnly(oneOf('draft', 'published', { default: 'draft' })),
  publishedAt: readOnly(optional(date())),
}) {}

Le schéma — déclaré une fois

class Post extends entity({
  id: primary(),
  title: text({ min: 1 }),
  status: readOnly(oneOf(
    'draft', 'published')),
}) {}

Post.validate(input)dérivée du shape · embarquée avec la classe

Surface d'API

post.list · post.publish

Table de base de données

auto-DDL → SQLite

Type TypeScript

function render(p: Post)

Contrat de formulaire

useFormFor(Post)

Type GraphQL

type Post { … }

Désignation & DI

useQuery(Post, 'list')

Un noyau, six projections — changez la déclaration, chaque projection suit.

Cette seule classe est à la fois :

  • le type TypeScript d'une ligne (function render(p: Post) — pas d'Infer<typeof …>),
  • le validateur des entrées client (Post.validate(input)),
  • la metadata que chaque adapter lit (Post.getFields()),
  • la désignation que les pages utilisent pour appeler les opérations (useQuery(Post, 'list')),
  • le nom nominal que l'injection de dépendances matche dans les signatures (user: User | null).

Le gradient. La logique métier vit dans des Fronds — des modules autonomes d'entités, handlers, collectors et seeds. Une Frond tourne in-process aujourd'hui et dans son propre process demain, derrière JSON-RPC 2.0, avec un code utilisateur identique. L'énoncé de topologie entier tient en une ligne de config :

// fougere.config.ts
remotes: { blog: 'http://127.0.0.1:4100' }

Pas de RPC sans voyage : un appel est une valeur (entity, operation, invocation) ; le runner l'exécute directement en mémoire quand la Frond est locale et la met sur le fil quand elle est distante. Les transports déplacent la valeur — ils ne la remodèlent jamais.

Donc le split coûte le saut et le JSON qui voyage avec, et rien d'autre : aucune sérialisation que le chemin local éviterait, aucun impôt du framework par-dessus le réseau.

Ordre de lecture

ConceptsPhilosophie · La Frond

Côté serveurDémarrer · App Nuxt existante · La CLI · Entités · Vues · Standard Schema · Handlers · Presenters · Collectors · Erreurs · Seeds · Les faits

Côté clientQueries & commands · Formulaires · Session · invoke

TopologieLe gradient · Surfaces · Déploiement

Statut. Fougere est en alpha : les paquets @fougere/* sont sur npm sous le tag alpha, et cette documentation décrit l'API telle qu'elle existe dans le dépôt aujourd'hui. Ce site tourne dessus.

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