What is Fougere

Fougere is a TypeScript framework built around two ideas.

Single-schema. One entity class declares your data once — and judges its own input: the same validate() runs in the browser and at the façade. That judge is itself a projection, derived from the shape axis, but a normative one shipped with the class: every other projection must agree with it, and it cannot drift on its own. SQLite tables, GraphQL types, form contracts and API surfaces are projections of that declaration — nothing is written twice.

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())),
}) {}

The schema — declared once

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

Post.validate(input)derived from the shape · ships with the class

API surface

post.list · post.publish

Database table

auto-DDL → SQLite

TypeScript type

function render(p: Post)

Form contract

useFormFor(Post)

GraphQL type

type Post { … }

Designation & DI

useQuery(Post, 'list')

One nucleus, six projections — change the declaration, every projection follows.

That single class is simultaneously:

  • the TypeScript type of a row (function render(p: Post) — no Infer<typeof …>),
  • the validator of client input (Post.validate(input)),
  • the metadata every adapter reads (Post.getFields()),
  • the designation pages use to call operations (useQuery(Post, 'list')),
  • the nominal name dependency injection matches in handler signatures (user: User | null).

The gradient. Business logic lives in Fronds — self-contained modules of entities, handlers, collectors and seeds. A Frond runs in-process today and in its own process tomorrow, behind JSON-RPC 2.0, with identical user code. The entire topology statement is one line of config:

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

There is no RPC without travel: a call is a value (entity, operation, invocation); the runner executes it directly in memory when the Frond is local and frames it onto the wire when it is remote. Transports move the value — they never reshape it.

So the split costs the hop and the JSON that rides it, and nothing else: no serialization the local path avoids, no framework tax layered on top of the network.

Reading order

ConceptsPhilosophy · The Frond

Server sideGetting started · Existing Nuxt app · The CLI · Entities · Views · Standard Schema · Handlers · Presenters · Collectors · Errors · Seeds · Facts

Client sideQueries & commands · Forms · Session · invoke

TopologyThe gradient · Surfaces · Deployment

Status. Fougere is in alpha: the @fougere/* packages are on npm under the alpha tag, and this documentation describes the API as it exists in the repository today. This site runs on it.

Built with Fougere — this site runs on the framework it documents.