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:
wf create project myapp
And out comes the shape we've been converging on all along:
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.pyis the app factory we adopted back in the Migrate chapter, under the name Migrate looks for:prepare_fluid.fluid/config.pyis the config class withAPP_CONFIGandAPP_FRONTEND.fluid/apiandfluid/appare the JSON and HTML routers, split out cleanly.fluid/models,schemas,services,events,utilsare the homes we kept reaching for as the app grew.fluid/frontend, the workspacepackage.jsonand 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:
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:
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.
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:
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.