litestar-org/litestar-skills/litestar-saq
Claude Code and agent skill by litestar-org/litestar-skills
- community
- ext:[{"type":"hook","description":"Pre/post lifecycle hooks"}]
Checking that the SKILL.md file is still in the repository.
Use when working with litestar_saq, SAQPlugin, SAQConfig, QueueConfig, TaskQueues, CronJob, litestar workers run, background jobs, schedules, or SAQ UI. Not for litestar-queues, Celery, RQ, or Dramatiq.
Install
npx vskill@latest install litestar-org/litestar-skills/litestar-saqSemi-extensibleMentions customization, but needs skill-specific knowledge. Read the extensible skills standard
SKILL.md
litestar-saq
litestar-saq is the first-party plugin that integrates SAQ (Simple Async Queue) with Litestar. It provides:
SAQPlugin— registers queues, workers, lifespan management, and DI forTaskQueuesSAQConfig/QueueConfig— declarative plugin, queue, worker, broker, shutdown, polling, and OpenTelemetry configurationlitestar workers run— CLI to start worker processes, optionally filtered by queue- Optional web UI mounted under the Litestar app
- DI injection of
TaskQueuesinto route handlers for ergonomic enqueueing
Code Style Rules
- Use PEP 604 unions:
T | None, neverOptional[T] - Async all I/O — task bodies and enqueue calls are
async def. - First positional arg of every task is
ctx: Context(fromsaq.types, notlitestar_saq.typing.Contextwhich is the OpenTelemetry context) orctx: dict[str, Any]. - Pass job payload as keyword arguments so task signatures and enqueue calls stay explicit.
- Use
NamedDependency[TaskQueues]for handler injection.TaskQueuesis registered under thetask_queuesdependency key, and Litestar ≥ 2.24 deprecates implicit DI.
Quick Reference
Plugin Setup (canonical pattern)
The canonical pattern from litestar-fullstack (src/py/app/server/plugins.py) uses lazy initialization and use_server_lifespan=True so worker child processes start and stop with the Litestar server lifespan.
Redis broker: Use this when Redis is already in the stack or is the chosen SAQ backend:
PostgreSQL broker: Install litestar-saq[psycopg] when PostgreSQL is the chosen backend. When sharing a database URL with SQLAlchemy (postgresql+psycopg:// or postgresql+asyncpg://), strip the driver suffix so QueueConfig.dsn starts with postgresql://:
Choose the broker already supported by the deployment. PostgreSQL job writes use SAQ's own pool and transaction; they are not automatically atomic with writes made through an application ORM or SQL session.
Each QueueConfig accepts exactly one connection source: a supported redis:// (rediss://, redis+unix://), postgresql://, or http:// dsn, or a supported broker_instance. Supplying both or neither raises ImproperlyConfiguredException. PostgreSQL DSNs must start with postgresql:// (postgres:// raises ImproperlyConfiguredException). PostgreSQL requires litestar-saq[psycopg]; configure queue behavior with broker_options (RedisQueueOptions or PostgresQueueOptions; note that on saq>=0.24, PostgresQueue.__init__ takes jobs_table, stats_table, and versions_table) and connection/client construction with broker_instance_options. In litestar-saq 0.8.0, set enable_otel=True explicitly when litestar-saq[otel] is installed (SAQPlugin.get_workers() evaluates should_enable_otel() without the Litestar app instance, so enable_otel=None resolves to False).
Wire into Litestar
Define a Task
Task functions live in app/domain/<domain>/tasks.py:
For long-running work, set the job's heartbeat stale threshold (at least 60s so it exceeds HeartbeatManager's default 30.0s batch flush interval) and decorate the task with monitored_job() so the plugin signals its batched HeartbeatManager while the task runs. heartbeat is not an update interval: SAQ marks an active job stuck when its last touch is older than that threshold.
Enqueue from a Handler (DI of TaskQueues)
When a handler creates or updates database rows that the background job will read, explicitly commit the database session when queue.enqueue() succeeds (or roll back when a duplicate key returns None) so the worker never races ahead of Litestar's before_send autocommit hook:
CLI
Web UI
When web_enabled=True, the SAQ web UI and JSON API are mounted at web_path (default "/saq"), including {web_path}/api/queues, job detail/retry/abort endpoints, and {web_path}/health. Protect the UI in production with web_guards=[...] and opt into OpenAPI schema inclusion with web_include_in_schema=True.
Job Options
| Option | Default | Use | |
|---|---|---|---|
timeout | 10 | Always set explicitly — SAQ's default is usually too low or too high for real jobs (0 disables) | |
retries | 1 | Retry count on exception | |
retry_delay | 0.0 | Seconds to wait before retrying | |
retry_backoff | False | True for exponential backoff with jitter, or a numeric maximum delay in seconds | |
ttl | 600 | Seconds to retain result after completion (0 retains indefinitely, -1 disables) | |
key | generated | Stable uniqueness key; enqueue returns None while that key already exists | |
heartbeat | 0 | Maximum seconds an active job may go without a touch; 0 disables stale detection | |
scheduled | 0 | Unix timestamp (epoch seconds) to delay start | |
priority | 0 | Dequeue priority on PostgreSQL queues (PostgresQueueOptions.priorities defaults to (0, 32767)) | |
group_key | None | Concurrency serialization key on PostgreSQL queues (at most one active job per group_key) | |
meta | {} | Arbitrary metadata dictionary attached to the job (CronJob also accepts meta) |
<workflow>
Workflow
Step 1: Install
Step 2: Define Queues
Build QueueConfig instances for each logical queue ("default", "emails", "reports"). Put exactly one of dsn or broker_instance on each QueueConfig. Reference task functions by dotted path or callable; the plugin imports dotted paths at startup. When passing lifecycle hooks (startup, shutdown, before_process, after_process) as dotted strings, always wrap them in a list (startup=["app.domain.system.tasks.worker_startup"]) because str is a Collection.
Step 3: Configure Plugin
Wrap QueueConfigs in SAQConfig. Pick a supported broker DSN (redis://..., postgresql://..., or http://...) that matches the deployment. Set use_server_lifespan=True when the web process should own worker child processes. Toggle web_enabled / web_guards for the introspection UI and enable_otel=True when litestar-saq[otel] is installed.
Step 4: Define Tasks
Place task functions in app/domain/<domain>/tasks.py. Use ctx: dict as the first argument and pass job data as keywords. Add shared resources (DB, HTTP client, email service) in QueueConfig.startup / before_process hooks and read them from ctx.
Step 5: Schedule Cron Work
This is the start of the file. Read the full SKILL.md on GitHub.
Security
- Security scanPassed, 100 of 100Pattern checks on the skill's files.
- Model reviewPassed
Quality evaluation
- Task evaluationNot runNo evaluation yet. Run one in Skill Studio.
Source
- SKILL.md in the repositoryCheckingskills/litestar-saq/SKILL.md
Popularity
- 7-day trend score
- 0
Not refreshed yet.
Extension points
hookPre/post lifecycle hooks
Works with
Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.
- Claude Code
- Cursor
- GitHub Copilot
- Windsurf
- Codex
- Gemini CLI
- Cline
- Amp
- Roo Code
- Goose
- Continue
- Kilo Code
- Junie
- Trae
- OpenHands
- Qwen Code
- AI Agent
- Tabnine Chat
- Sourcegraph Cody
- Amazon Q
- Firebase Genkit
- Sweep
- Devin
- Phind
- Mentat
- Claude Desktop
- Aider
- GPT Pilot
- Plandex
- bolt.new
- v0
- Replit Agent
- Anse
- TypingMind
- BetterChatGPT
- LibreChat
- Warp AI
- Pieces
- Fig AI
Embed badge
[](https://verified-skill.com/skills/litestar-org/litestar-skills/litestar-saq)