CLI

Create

This is the chapter the whole guide has been quietly building toward. Every structure we assembled by hand — the config, the routers, the models, the services, the frontend workspace — is what a single wf create project lays on the table in one go. Let's meet it.

wf create project

One command scaffolds a complete, opinionated project and walks you through the frontend choice interactively:

terminal bash
wf create project myapp

And out comes the shape we've been converging on all along:

myapp/ text
myapp/
├── main.py                 # prepare_fluid(), includes the routers
├── package.json            # the npm workspace
├── vite.config.js          # the orchestrator, written for you
├── .gitignore
├── additives/              # your Additives live here
└── fluid/
    ├── config.py           # register_config Config (APP_CONFIG, APP_FRONTEND)
    ├── api/
    │   ├── __init__.py      # api_router (prefix /api)
    │   ├── health.py        # a ready-made health handler
    │   └── v1/
    │       └── __init__.py  # v1 router (prefix /v1)
    ├── app/                 # HTML routes (index.py) — for htmx / none
    ├── frontend/            # Vite workspace — for the vite type
    ├── models/              # SQLAlchemy models
    ├── schemas/             # pydantic schemas
    ├── services/            # business logic
    ├── events/              # event + query handlers
    ├── utils/               # your helpers
    ├── static/
    │   ├── css/tailwind_raw.css
    │   ├── img/
    │   └── js/
    └── templates/
        └── index.html       # extends fluid_base.html

If that layout looks familiar, it should — you've built every part of it across the last chapters:

  • main.py is the app factory we adopted back in the Migrate chapter, under the name Migrate looks for: prepare_fluid.
  • fluid/config.py is the config class with APP_CONFIG and APP_FRONTEND.
  • fluid/api and fluid/app are the JSON and HTML routers, split out cleanly.
  • fluid/models, schemas, services, events, utils are the homes we kept reaching for as the app grew.
  • fluid/frontend, the workspace package.json and the Tailwind entry point are the surface we wired up by hand.

A config that survives a public repo

One small convention comes with the generated config, and it is worth adopting. The Config class inherits from a MyConfig that the framework tries to import from fluid/_my_config.py — and falls back to an empty class when there is none. That file is in the generated .gitignore:

fluid/config.py python
from webfluid.core.config import register_config

# The MyConfig class is our convention for developing public git repos.
try: from fluid._my_config import MyConfig
except ImportError:
    class MyConfig: pass

@register_config(10)
class Config(MyConfig):
    APP_CONFIG = {
        "title": "myapp",
        "version": "1.0.0"
    }
    APP_FRONTEND = { "type": "htmx", "alpine": True }

So the settings everyone shares live in config.py and go into git, while anything local or private lives in _my_config.py and never does. No second config mechanism is involved — the config builder simply walks the class hierarchy, so a key only MyConfig defines is picked up as if it had been written in Config. Keys that appear in both belong to Config: the subclass wins, as it would anywhere else in Python. Clone the repo without that file and the app still starts.

 

The frontend type you pick decides two details: choose vite and you get the fluid/frontend workspace (the app/ folder is dropped, since the Vite index owns / unless you answered no to register_index); choose htmx or none and you get the app/index.py HTML route with a ready index.html instead. Useful flags: --skip-defaults, --skip-frontend and --babel-fallback.

wf create app

A project can serve many apps, and each app is just a config. wf create app builds one interactively — it generates a secret, asks for your database and redis uris, and lets you tick the extensions, features and Additives to enable. It even pulls in any questions your Additives contribute through their configure setup:

terminal bash
wf create app prod

The result is a fully populated app_configs/prod.ini — the same kind of file we hand-wrote in Getting Started, only now filled in for you with a real secret key and every switch in place.

 

Every file the scaffolders write is explicitly UTF-8, the app config included — and wf run and wf migrate read it back UTF-8 first, falling back to the local encoding so a config written by an older release still opens. That symmetry is the whole point: a password with an umlaut in it should survive being generated on a Windows console and deployed from a Linux one.

 

The fallback can recover an old config, but only on a machine whose locale encoding is the one that wrote it. If you have a pre-beta-3 config carrying a non-ASCII value, rewrite it once — or keep such values ASCII and point at them through a *_FILE key, which is resolved from disk rather than from the ini and never had the problem.

wf create additive

And the third scaffolder closes the loop on the Additives chapters. wf create additive asks for an id, a version format, whether it's a base or should extend one, its frontend, and which extensions it requires — then writes the whole package, manifest and all:

terminal bash
wf create additive portal
 

That's the payoff of the red thread: the framework's conventions aren't a cage, they're a destination. You can build every piece by hand — and now that you understand each one, you can let the CLI lay them down for you and get straight to the interesting part.

Continue reading

From here you can continue straight with Run.