{"id":48915591,"url":"https://github.com/mahdilamb/redis-queen","last_synced_at":"2026-04-17T02:34:08.211Z","repository":{"id":350837326,"uuid":"1208456395","full_name":"mahdilamb/redis-queen","owner":"mahdilamb","description":"Redis schema migration tool for Pydantic models","archived":false,"fork":false,"pushed_at":"2026-04-12T12:06:58.000Z","size":105,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-12T12:07:49.851Z","etag":null,"topics":["migrations","pydantic","python","redis","schema-migration"],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mahdilamb.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-12T10:01:06.000Z","updated_at":"2026-04-12T11:15:33.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/mahdilamb/redis-queen","commit_stats":null,"previous_names":["mahdilamb/red-queen"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/mahdilamb/redis-queen","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mahdilamb%2Fredis-queen","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mahdilamb%2Fredis-queen/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mahdilamb%2Fredis-queen/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mahdilamb%2Fredis-queen/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mahdilamb","download_url":"https://codeload.github.com/mahdilamb/redis-queen/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mahdilamb%2Fredis-queen/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31912513,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-16T18:22:33.417Z","status":"online","status_checked_at":"2026-04-17T02:00:06.879Z","response_time":62,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["migrations","pydantic","python","redis","schema-migration"],"created_at":"2026-04-17T02:34:04.751Z","updated_at":"2026-04-17T02:34:08.151Z","avatar_url":"https://github.com/mahdilamb.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# redis-queen\n\n[![CI](https://github.com/mahdilamb/redis-queen/actions/workflows/ci.yml/badge.svg)](https://github.com/mahdilamb/redis-queen/actions/workflows/ci.yml)\n[![PyPI](https://img.shields.io/pypi/v/redis-queen)](https://pypi.org/project/redis-queen/)\n[![Python](https://img.shields.io/pypi/pyversions/redis-queen)](https://pypi.org/project/redis-queen/)\n[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)\n\nSchema migration tool for Redis-backed Pydantic models. Track model changes, generate versioned migration scripts, and apply them using async SCAN-based key iteration.\n\n## Installation\n\nWith uv (recommended):\n\n```bash\nuv add redis-queen          # library only\nuv add \"redis-queen[cli]\"   # include CLI\n```\n\nWith pip:\n\n```bash\npip install redis-queen\npip install \"redis-queen[cli]\"  # include CLI\n```\n\nIn `pyproject.toml`:\n\n```toml\ndependencies = [\"redis-queen\"]          # library only\ndependencies = [\"redis-queen[cli]\"]     # include CLI\n```\n\n## Quick start\n\n### 1. Decorate your models\n\n```python\nfrom pydantic import BaseModel, Field, RootModel\nfrom redis_queen import redis_queen, migrates_from\nfrom typing import Annotated\n\n@redis_queen(key=\"user:{user_id}:profile:\")\nclass UserProfile(BaseModel):\n    name: str\n    email: str\n\n@redis_queen(key=\"agent:session:{session_id}:display\")\nclass DisplayMessages(RootModel[list[DisplayMessage]]):\n    \"\"\"Stored as a JSON array.\"\"\"\n\n# Field renames\n@redis_queen(key=\"item:{item_id}\")\nclass Item(BaseModel):\n    title: Annotated[str, migrates_from(\"name\")]  # tracks rename from \"name\"\n```\n\nKey patterns use f-string style placeholders. `match` is auto-derived by replacing `{...}` with `*` for SCAN.\n\n### 2. Configure in pyproject.toml\n\n```toml\n[tool.redis-queen]\nmigrations_dir = \"migrations\"\ndeletion_protection = false  # or true, or an integer (TTL in seconds)\n\n[tool.redis-queen.profiles.default]\ndefault = true\nhost = \"localhost\"     # defaults to localhost\nport = 6379            # defaults to 6379\ndb = 0                 # defaults to 0\nmigrations_collection = \"my_collection\"\nmodel_search_path = [\"myapp.models\"]\n```\n\n`host`, `port`, and `db` default to `localhost`, `6379`, and `0`. All string values support `$ENV_VAR` expansion.\n\n**Profile resolution order:** `--profile` CLI flag \u003e `REDIS_QUEEN_PROFILE` env var \u003e profile with `default = true`.\n\nMultiple profiles (e.g. local + docker):\n\n```toml\n[tool.redis-queen.profiles.default]\ndefault = true\nmigrations_collection = \"agent\"\nmodel_search_path = [\"agent.types\"]\n\n[tool.redis-queen.profiles.docker]\nhost = \"redis\"\nmigrations_collection = \"agent\"\nmodel_search_path = [\"agent.types\"]\n```\n\n### 3. Generate and apply migrations\n\n```bash\n# Create the initial schema snapshot\nredis-queen revision -m \"initial\"\n\n# Show current state\nredis-queen show\n\n# After changing models, generate a new revision\nredis-queen revision -m \"add email field\"\n\n# Check if there are pending changes (exit 1 if none, useful in CI)\nredis-queen revision --check\n\n# Show what changed\nredis-queen diff\n\n# Apply pending migrations (stages first, prompts for confirmation)\nredis-queen up\n\n# Apply without confirmation\nredis-queen up --auto-apply\n\n# Downgrade to a specific revision\nredis-queen down 0001\nredis-queen down --root  # revert all\n\n# Reset to a specific revision (up or down as needed)\nredis-queen reset 0002\nredis-queen reset --root\n```\n\nAll mutation commands (`up`, `down`, `reset`) support `--auto-apply` to skip the staging confirmation prompt.\n\n## Model discovery\n\n`model_search_path` accepts:\n\n- **Directories:** `\"src/myapp/models\"` -- walks all `.py` files\n- **Single files:** `\"src/myapp/models.py\"`\n- **Dotted module paths:** `\"myapp.models\"` -- imports the module and recursively walks all submodules\n\n## Schema tracking\n\nSnapshots use `model_json_schema()`, which captures the full recursive schema including nested models. A change to any nested model triggers a new revision.\n\n## Generated migrations\n\nRevisions auto-generate `upgrade` and `downgrade` functions:\n\n- **Field added with default** -- sets the default value\n- **Field added, optional (factory)** -- infers zero-value from type (`[]`, `{}`, `\"\"`, `0`, etc.)\n- **Field added, required, no default** -- `# TODO` comment\n- **Field removed** -- backs up values, deletes from data\n\n## Deletion protection\n\nControls what happens when fields are removed:\n\n```toml\n[tool.redis-queen]\ndeletion_protection = false  # backup without TTL (default)\ndeletion_protection = true   # generate TODO, don't delete\ndeletion_protection = 3600   # backup with 1-hour TTL\n```\n\nWhen using an integer TTL, backups expire after the specified seconds. If a downgrade runs after the TTL, a warning is emitted for each key where the backup has expired and fields cannot be restored.\n\n## Python API\n\n```python\nfrom redis_queen import (\n    redis_queen,\n    migrates_from,\n    auto_migrate_up,\n    migrate_up,\n    migrate_down,\n    apply_plan,\n    find_one,\n    get_one,\n)\n```\n\n### Auto-migrate on startup\n\n```python\nfrom redis_queen import auto_migrate_up\n\n# Resolves config, connects to Redis, applies all pending migrations.\n# Uses REDIS_QUEEN_PROFILE env var or default profile.\nawait auto_migrate_up()\n```\n\n### FastAPI lifespan example\n\n```python\nfrom redis_queen import auto_migrate_up\n\n@asynccontextmanager\nasync def lifespan(app: FastAPI):\n    await auto_migrate_up()\n    yield\n```\n\n### Query utilities\n\n```python\n# find_one: format key pattern with args/kwargs, then GET\nprofile = await find_one(UserProfile, \"123\")\nprofile = await find_one(UserProfile, user_id=\"123\")\nraw = await find_one(UserProfile, \"123\", return_raw=True)\n\n# get_one: GET by full literal key\nprofile = await get_one(UserProfile, \"user:123:profile:\")\nraw = await get_one(UserProfile, \"user:123:profile:\", return_raw=True)\n```\n\n### Low-level API\n\nFor full control, use `migrate_up` / `migrate_down` directly with an explicit Redis client, migrations dir, and model list. See the CLI command implementations for examples.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmahdilamb%2Fredis-queen","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmahdilamb%2Fredis-queen","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmahdilamb%2Fredis-queen/lists"}