Skip to content

Part 1: Setup

Scaffold the project

python -m venv .venv && source .venv/bin/activate
pip install zeython
zeython new "TaskFlow"
cd task_flow
pip install -e ".[dev]"
cp .env.example .env

zeython new generated a real, runnable project — not a single-file demo. (The [dev] extra pulls in pytest, pytest-asyncio, and httpx — the project ships with a passing test in tests/test_home.py already, and you'll write more of your own starting in Part 6.) Look at what's there:

task_flow/
├── app/
│   ├── Controllers/       # request handlers
│   ├── Models/            # async Active Record models
│   ├── Middleware/        # ASGI middleware
│   ├── Providers/         # your own service providers
│   ├── Jobs/               # background jobs
│   └── Console/Commands/   # custom `zeython command <name>` CLI commands
├── database/
│   ├── factories/          # model factories for tests/seeding
│   └── seeders/            # `zeython db seed`
├── routes/
│   └── web.py               # route definitions
├── migrations/               # Alembic migrations
├── tests/
├── main.py                   # application entry point -- start here
├── alembic.ini
└── .env.example

main.py is worth opening now. It's not hidden framework magic — it's plain Python that constructs an Application, registers the providers your app needs (AuthServiceProvider, DatabaseServiceProvider, RouteServiceProvider, ...), and a dozen more commented out that you can turn on later (rate limiting, an admin panel, localization). Everything your generated app does, it does because a line in main.py says so — see Architecture when you're ready for the full picture of how the container and providers fit together. For now, the short version: a service provider is where a piece of functionality (auth, the database, routing) gets wired into the app, and main.py is the list of which pieces you're using.

Run it

A fresh scaffold ships with the User model already written but no migration file for it yet — revision autogenerates one by diffing your models against the (empty) database, migrate applies it:

zeython db revision -m "create users table"
zeython db migrate
zeython serve

Visit http://127.0.0.1:8000 — you'll get a small JSON welcome payload. zeython serve auto-reloads on file changes, so leave it running for the rest of this tutorial.

Try the auth that's already there

A generated project ships with a working User model and session authentication out of the box (see Authentication for the full picture later) — you get to use it for free instead of building login from scratch:

curl -sS -c cookies.txt -X POST http://127.0.0.1:8000/register \
  -H 'Content-Type: application/json' \
  -d '{"name": "Ada", "email": "ada@example.com", "password": "hunter2222"}'
{"name":"Ada","email":"ada@example.com","id":1,"created_at":"...","updated_at":"...","is_deleted":false,"deleted_at":null}

-c cookies.txt saves the session cookie /register sets — you're now logged in as Ada, and every following curl in this tutorial that passes -b cookies.txt will be authenticated as her. Confirm it:

curl -sS -b cookies.txt http://127.0.0.1:8000/me
{"name":"Ada","email":"ada@example.com","id":1,"created_at":"...","updated_at":"...","is_deleted":false,"deleted_at":null}

That's the whole account system you'd otherwise hand-roll — password hashing, a signed session cookie, CSRF protection on every unsafe request from here on (you'll deal with that directly in Part 5, when it's TaskFlow's own routes that need protecting) — already wired up, already tested, already documented.

Next: Part 2 — Models, where TaskFlow actually starts.