Skip to content

Typer

Mount CLI commands that expose your key store over the command line. The CLI wires the service, repository, and hashers together using Typer dependency injection.

Features

  • One call to create_api_keys_cli registers create, list, get, update, delete and verify commands.
  • Depends on an async session factory (see async_sessionmaker).
  • Shares a single ApiKeyHasher instance across requests.

Example

This is the canonical example from examples/example_cli.py.

Always set a pepper

The default pepper is a placeholder. Set API_KEY_PEPPER (or pass it explicitly to the hashers) in every environment.

import os
from contextlib import asynccontextmanager
from pathlib import Path
from typing import AsyncIterator

from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine
from sqlalchemy.orm import DeclarativeBase

from fastapi_api_key import ApiKeyService
from fastapi_api_key.cli import create_api_keys_cli
from fastapi_api_key.hasher.argon2 import Argon2ApiKeyHasher
from fastapi_api_key.repositories.sql import SqlAlchemyApiKeyRepository, ApiKeyModelMixin


class Base(DeclarativeBase): ...


class ApiKeyModel(Base, ApiKeyModelMixin): ...


# Set env var to override default pepper
# Using a strong, unique pepper is crucial for security
# Default pepper is insecure and should not be used in production
pepper = os.getenv("API_KEY_PEPPER")
hasher = Argon2ApiKeyHasher(pepper=pepper)

path = Path(__file__).parent / "db.sqlite3"
database_url = os.environ.get("DATABASE_URL", f"sqlite+aiosqlite:///{path}")

async_engine = create_async_engine(database_url, future=True)
async_session_maker = async_sessionmaker(
    async_engine,
    class_=AsyncSession,
    expire_on_commit=False,
)


@asynccontextmanager
async def service_factory() -> AsyncIterator[ApiKeyService]:
    """Yield an ApiKeyService backed by the SQLite SQLAlchemy repository."""
    async with async_engine.begin() as conn:
        await conn.run_sync(Base.metadata.create_all)

    async with async_session_maker() as async_session:
        repo = SqlAlchemyApiKeyRepository(async_session=async_session)
        service = ApiKeyService(repo=repo, hasher=hasher)
        try:
            yield service
            await async_session.commit()
        except Exception:
            await async_session.rollback()
            raise


app = create_api_keys_cli(service_factory)

if __name__ == "__main__":
    # Run the CLI with `uv run examples/example_cli.py`
    app()

Commands exposed

Command Description
create Create a new API key
list List all API keys
show <key_id> Get details of a specific API key
delete <key_id> Delete an API key
verify <api_key> Verify an API key
update <key_id> Update an existing API key