Skip to content
Guide

Database setup

FastAuth creates its tables, six standard roles, and a superadmin user for you, from the command line or at app startup.

What gets created#

FastAuth works with your existing SQLModel engine rather than a database of its own, so initialization adds a small number of tables alongside whatever you already have.

TableWhat it holds
userOne row per account: username, email, hashed_password, disabled, email_verified, token_version
roleOne row per role, with a name and description
userrolelinkThe join between users and roles: one row per role a user holds

Three columns on user do more work than their names suggest:

  • hashed_password is a bcrypt hash, never a password. It includes the salt, which is why the rows look like random noise even for identical passwords. See how auth works for what that means.
  • disabled locks an account out at every step — login, refresh, and every protected route — without deleting anything.
  • token_version is stamped into each issued token. Raising it invalidates every token that user holds, which is how POST /logout/all and password changes sign someone out everywhere.

CLI initialization#

shell
# Point it at your app file, settings are detected automatically
fastauth app.py

# Or pass everything explicitly
fastauth --db-url="sqlite:///./app.db" --secret-key="your-secret-key"

# Choose the superadmin credentials
fastauth app.py --username="admin" --password="secure_password"

# Run individual steps
fastauth app.py --init-db --init-roles --create-superadmin

Where the CLI looks for settings#

You rarely need to pass --db-url or --secret-key, because the CLI finds them. It checks these sources in order and stops at the first hit:

OrderSource
1The flags you passed, --db-url and --secret-key
2Real environment variables: DATABASE_URL, SECRET_KEY
3A .env file in the current or parent directory
4The app file you named
5config.py, settings.py, db.py, database.py, models.py beside it

For the last two it reads assignments directly, and if that finds nothing it imports the module and inspects it — so a live engine object works even when its URL is built at runtime.

The environment always beats .env. A .env file only fills in what the environment has not already set. This matters in deployment: if a .env could override a real SECRET_KEY, a stale file left in an image would sign every token with the wrong key, and nothing would tell you. Before v0.8.0 .env did win, so upgrade if you deploy with both.

Programmatic initialization#

Running it in the app's lifespan means a fresh checkout or a new deployment comes up ready, with no separate setup step to forget.

main.py
from contextlib import asynccontextmanager

@asynccontextmanager
async def lifespan(app: FastAPI):
    auth.initialize_db(
        create_tables=True,           # create database tables
        init_roles=True,              # create the six standard roles
        create_admin=True,            # create a superadmin if none exists
        admin_username="superadmin",
        admin_password="admin123",    # change in production!
    )
    yield

app = FastAPI(lifespan=lifespan)

Each step is idempotent — running it twice leaves the same result as running it once. Tables are only created if missing, roles are skipped if already present, and the superadmin is only created when no admin exists. So calling it on every boot is safe, and there is no need to guard it with a "first run" check.

Pass admin_username and admin_password explicitly during app startup. Otherwise FastAuth prompts on the console and startup blocks waiting for input — which, on a server with no attached terminal, looks exactly like a hang.

Schema changes later#

SQLModel's create_all only creates tables that do not exist. It never alters one that does. Add a column to your user model and it will simply not appear, and you will get a no such column error at runtime.

While developing against SQLite, deleting the database file and letting it rebuild is the fastest fix. Once you have data worth keeping, use Alembic migrations. The same applies to upgrading FastAuth itself: v0.6.0 added email_verified and token_version, so databases created before it need those columns added. See production.