Build a mobile friendly web app. Battery included: Wiring signups, feature subscriptions, and payment.
- FastAPI (API backend)
- fastapi-sso (Authentication — Google, GitHub, LinkedIn, or Auth0)
- Stripe (Payment)
- SendGrid (Email)
I wanted to build a web app with frontend, backend, database, login, email services, and paywall very quickly.
Since those are common skeleton for most SaaS apps, I built a plugin framework to build apps quickly.
Each feature lives in its own directory under app/plugins/ and owns its
models, schemas, crud, and routers — keeping concerns separated and
making it easy to add, remove, or disable features without touching unrelated
code.
Plugins are discovered automatically at boot time: any subdirectory with an
__init__.py is imported. To disable a plugin without deleting it, drop a
.zixignore file inside it.
Performance: plugin discovery and import happens once at server startup.
Python caches every imported module in sys.modules, so there is no
per-request import overhead. Adding more plugins does not affect runtime
performance.
Cross-plugin dependencies (e.g. auth importing from users) are
explicit — you reference the plugin by name. Keep these dependencies
minimal and one-directional to avoid tight coupling.
- A supported Python version (see Python release schedule)
- uv (recommended package manager)
Create a project directory and set up a Python environment with uv:
pip install uv
mkdir zix_projects
cd zix_projects
uv initInstall zix:
uv add zixwebImportant: run zix init from the project root (e.g. zix_projects/), not from inside the app folder.
echo "y" | uv run zix init -w myappThe CLI prompts for confirmation on stdin. Pipe
echo "y"to avoid anEOFErrorin non-interactive shells.
Navigate to the project root and run Alembic to create the schema before starting the server for the first time. Skipping this causes a 500 error on every request because the auth middleware queries the token table before it exists.
cd myapp
uv run alembic revision --autogenerate -m "initial"
uv run alembic upgrade head
cd ..The app/static/compiled/ directory is empty until you run the compile script. The server will fail to start without it.
cd myapp
bash bin/compile
cd ..
bin/compilealso copiesapp/static/assets/js/_config.jstocompiled/assets/js/config.js. The Bootstrap Studio export includes an emptyconfig.jsplaceholder — without this copy step the browser throwsUncaught ReferenceError: Config is not defined.
Copy the env template and fill in your credentials:
mkdir -p myapp/.env
cp myapp/env.yml myapp/.env/env.ymlOpen myapp/.env/env.yml and configure the auth backend (see Authentication below).
Important: always run the server from the project root (e.g. zix_projects/), not from inside myapp/. Running from inside the app directory causes ModuleNotFoundError: No module named 'config'.
uv run zix serve -w myapp -p 4000Point your browser to http://localhost:4000.
By default zix uses fastapi-sso (open source, no external account required) with Google SSO enabled. You can switch to Auth0 if you prefer a managed identity service.
fastapi-sso provides OAuth2 login via Google, GitHub, and LinkedIn without running a separate identity server.
Supported providers and toggle flags in env.yml:
| Provider | Toggle flag | Default |
|---|---|---|
USE_GOOGLE_SSO |
"true" |
|
| GitHub | USE_GITHUB_SSO |
"false" |
USE_LINKEDIN_SSO |
"false" |
Each provider requires its own OAuth app credentials. Example env.yml section:
local:
USE_AUTH0: "false"
USE_GOOGLE_SSO: "true"
GOOGLE_CLIENT_ID: "your-client-id.apps.googleusercontent.com"
GOOGLE_CLIENT_SECRET: "your-client-secret"
USE_GITHUB_SSO: "false"
GITHUB_CLIENT_ID: "your-github-client-id"
GITHUB_CLIENT_SECRET: "your-github-client-secret"
USE_LINKEDIN_SSO: "false"
LINKEDIN_CLIENT_ID: "your-linkedin-client-id"
LINKEDIN_CLIENT_SECRET: "your-linkedin-client-secret"Login routes when using fastapi-sso:
| URL | Provider |
|---|---|
/login or /login/google |
|
/login/github |
GitHub |
/login/linkedin |
OAuth app setup: for each enabled provider, register an OAuth app and set the callback URL to http://localhost:4000/callback/<provider> (e.g. /callback/google).
To use Auth0 instead, set USE_AUTH0: "true" in env.yml and fill in the Auth0 credentials:
local:
USE_AUTH0: "true"
AUTH0_CLIENT_ID: "your-auth0-client-id"
AUTH0_CLIENT_SECRET: "your-auth0-client-secret"
AUTH0_DOMAIN: "example.us.auth0.com"In your Auth0 dashboard:
- Go to Applications and select your app.
- Set Application Type to "Regular Web Application."
- Add
http://localhost:4000/callbackto Allowed Callback URLs, Allowed Logout URLs, and Allowed Web Origins. - Click Save.
When USE_AUTH0: "true", the fastapi-sso provider flags (USE_GOOGLE_SSO, etc.) are ignored.
Edit myapp/app/static/compiled/index.html directly for quick changes, then restart the server.
For structured frontend work, use the Bootstrap Studio project under myapp/bstudio/. Set the export path to myapp/app/static/compiled/ in Bootstrap Studio's Export Settings, then export and run bash bin/compile afterward.
Place any static files under myapp/app/static/compiled/ — they are served from / as long as the path does not conflict with an API endpoint.
Edit myapp/app/static/assets/js/_config.js to customize the app's navigation pages and settings (this is the source of truth for config.js).
uv run zix add-plugin -w myappThe CLI will prompt for a plugin name. The plugin skeleton is created at myapp/app/plugins/<plugin_name>/. See the generated README.md inside the plugin directory to get started.
SQLite is used by default (creates myapp/zix.db). For production, switch to PostgreSQL by setting these variables in env.yml:
DATABASE: "your_database_name"
DB_HOST: "your-db-host"
DB_USERNAME: "your-db-username"
DB_PASSWORD: "your-db-password"Set STRIPE_API_KEY and STRIPE_API_SECRET in env.yml.
Set SENDGRID_KEY, SENDGRID_FROM_EMAIL, and related template IDs in env.yml.
zix apps can be deployed to any cloud VM. A Dockerfile is included in the generated project root for containerized deployments.
To be written.
To be written.