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.