Getting started
Create an app:
npm create fougere
pnpm create fougere
bun create fougere
yarn create fougere
The project is written for the package manager that ran the command — see the CLI.
Alpha. The
@fougere/*packages are on npm under thealphatag: the version is the whole promise, and the surface can still move. This site (site/) anddemos/nuxt-blogare complete references. Already have a Nuxt app? Add Fougere to it.
demos/nuxt-blog43 filesimport { entity, primary, text } from '@fougere/schema';
export default class Author extends entity({
id: primary(),
name: text({ min: 1, max: 100 }),
email: text({ min: 3 }),
}) {}
What an app looks like
You get a Nuxt app, a fronds/ directory and one config file:
my-app/
fougere.config.ts ← database, auth, where each part runs
fronds/
blog/ ← one domain of your app
entities/Post.ts
handlers/PostHandler.ts
collectors/CurrentUserCollector.ts
seeds/Post.seed.ts
user/ ← another one
entities/User.ts
apps/
nuxt/
nuxt.config.ts ← modules: ['@fougere/nuxt']
app/pages/ ← ordinary Nuxt pages
Each directory under fronds/ is a frond: one domain of your app, with its data and
what it does. Inside it, the folder a file sits in says what it is: an entity in
entities/, a handler in handlers/, a service in services/, and so on. You never
register anything: Fougere finds each class by where it lives and what it is called.
A page imports from a frond by its name:
import Post from '@fronds/blog/entities/Post.js';
The @fronds/blog alias exists on its own, for every frond. You add no package.json and
no workspace entry. A frond needs its own package.json only to be renamed or imported from
outside a Nuxt app: see Frond — naming and importing.
fougere new puts the app under apps/<name>/ and the fronds at the workspace root, so
several apps can share the same domains. The app finds them on its own.
fougere.config.ts — reference
import '@fougere/adapter-sql';
import { defineFougere } from '@fougere/core';
import { betterAuth } from '@fougere/auth-better';
import User from './fronds/user/entities/User';
export default defineFougere({
// Where rows live. The entry that lists no entities holds every entity the others do not.
// { type: 'sqlite', path: '…' } → a SQLite file
// { type: 'pg', url: '…' } → Postgres, once `pg` is installed (mysql, mssql alike)
// no `sources:` at all → rows in memory, on every host, and the boot says so
// The import above is what makes these types available — nothing is imported for you.
sources: {
main: { type: 'sqlite', path: '.data/app.db' },
// archive: { type: 'sqlite', path: '/mnt/legacy/catalog.db', entities: ['Book'] },
},
fronds: {
// Topology. A Frond listed here is STILL scanned (its entities keep feeding
// metadata to forms and validation) but is not hosted locally: calls travel
// as JSON-RPC to the process that hosts it. Comment the line → in-process.
// blog: 'http://127.0.0.1:4100',
// Auth (optional) — better-auth behind a thin translation layer. It is a frond like any
// other: an entry of `fronds:`, left out by `only:` wherever the engine should not start.
auth: betterAuth({
user: User, // your entity extending AuthUser
secret: process.env.AUTH_SECRET!, // 32+ chars
baseUrl: process.env.SITE_URL ?? 'http://localhost:3000',
basePath: '/auth', // mounts /auth/** (sign-in, sign-up, sign-out…)
trustedOrigins: ['http://localhost:3000'],
sessionTtl: 30 * 24 * 60 * 60 * 1000,
providers: {
credential: { minPasswordLength: 8, autoSignIn: true },
// google: { clientId: …, clientSecret: … },
},
}),
},
});
Tables are derived from your entities, and written by fougere migrate — never by the
boot. On your database, --latest follows the entities as they are; every other database
receives only what fougere freeze recorded. A boot reads the database against the entities and refuses when it is behind, naming each
missing table and column. A rename is declared on the entity (previous:), see
How a schema moves. Seeds run once the schema check passes and write
their rows by the storage, which still judges them.
A second database is a second entry of sources:, listing the entities it holds, and a Frond
that must query across them declares reads: — see Sources.
Run
npm install
npm run migrate # fougere migrate --latest --apply — create what the entities declare
npm run dev # scan → write the statement → check the schema → seed → serve on :3000
pnpm install
pnpm migrate # fougere migrate --latest --apply — create what the entities declare
pnpm dev # scan → write the statement → check the schema → seed → serve on :3000
bun install
bun run migrate # fougere migrate --latest --apply — create what the entities declare
bun run dev # scan → write the statement → check the schema → seed → serve on :3000
yarn install
yarn migrate # fougere migrate --latest --apply — create what the entities declare
yarn dev # scan → write the statement → check the schema → seed → serve on :3000
The last lines fougere new prints are already written for your package manager.
The first page says which host serves the app and links to the three guides: business logic, data, topology.
Check the boot log for the number of discovered Fronds:
INF [boot] ready in 483ms — 3 frond(s)
Next: Entities — the field vocabulary and the four axes.