Last modified: Aug 12, 2025 By Alexander Williams
Python Typer Async Command Support Guide
Python Typer makes CLI development easy. Adding async support takes it further. This guide covers async commands with async def
and event loops.
Why Use Async in Typer?
Async commands help with I/O-bound tasks. They prevent CLI apps from blocking. This improves performance for network calls or file operations.
Typer integrates with asyncio. It automatically handles the event loop. You focus on writing async logic.
Basic Async Command Setup
Use async def
instead of regular def
for commands. Typer detects and runs them properly.
import typer
import asyncio
app = typer.Typer()
@app.command()
async def fetch_data(url: str):
"""Fetch data from URL"""
# Simulate async network call
await asyncio.sleep(1)
typer.echo(f"Data from {url} fetched")
if __name__ == "__main__":
app()
$ python app.py fetch-data "https://example.com"
Data from https://example.com fetched
Event Loop Handling
Typer manages the asyncio event loop automatically. No manual loop creation needed. It works like magic.
Important: Don't create your own loop. Typer handles it internally. This prevents conflicts.
Async with Synchronous Code
Mix async and sync commands freely. Typer routes each properly. Here's an example:
@app.command()
def sync_command():
"""Regular sync command"""
typer.echo("This runs synchronously")
@app.command()
async def async_command():
"""Async command"""
await asyncio.sleep(0.5)
typer.echo("This runs asynchronously")
Error Handling in Async Commands
Handle errors like in regular async code. Use try/except blocks. For more on errors, see our Python Typer Error Handling Guide.
@app.command()
async def risky_operation():
try:
await potentially_failing_operation()
except Exception as e:
typer.echo(f"Error: {e}", err=True)
raise typer.Exit(1)
Performance Considerations
Async commands shine with I/O operations. For CPU-bound tasks, consider threads instead. Check our performance optimization guide.
Remember: Async doesn't mean faster for all cases. Use it wisely.
Testing Async Commands
Test async commands like regular ones. Typer's test runner handles async automatically. See CLI testing guide for details.
Real-world Example
Here's a practical async command fetching multiple URLs:
import httpx
@app.command()
async def fetch_urls(urls: list[str]):
"""Fetch multiple URLs concurrently"""
async with httpx.AsyncClient() as client:
tasks = [client.get(url) for url in urls]
responses = await asyncio.gather(*tasks)
for r in responses:
typer.echo(f"URL: {r.url} - Status: {r.status_code}")
Conclusion
Typer's async support makes powerful CLI apps easy. Use async def
for I/O-bound tasks. Let Typer handle the event loop.
Combine async commands with Typer's other features. Build efficient and responsive CLI tools. The possibilities are endless.