N
NexusDigitalLabs
← Back to Academy

Phase 1 — Foundations · Lesson 9 · 40 XP

Project: concurrent fetcher with retries, timeouts, validation

This is the Phase 1 project from the roadmap — it doesn't introduce new syntax, it combines Lessons 6-8 into one real piece of software: concurrent HTTP calls (async httpx), that don't hang forever (timeouts), that recover from transient failures (retries with backoff), and that never trust what came back without checking (Pydantic validation).

A timeout caps how long any single request can block — without one, a single unresponsive server can stall your whole batch indefinitely. A retry-with-backoff waits a little longer after each failure (1s, then 2s, then 4s) instead of hammering an already-struggling server immediately, which is more likely to make things worse, not better.

async def fetch_with_retry(client, url, attempts=3):
    for attempt in range(attempts):
        try:
            r = await client.get(url, timeout=5.0)
            r.raise_for_status()
            return r.json()
        except (httpx.HTTPError, httpx.TimeoutException):
            if attempt == attempts - 1:
                raise
            await asyncio.sleep(2 ** attempt)

Exercise

Build the full project: fetch 50 URLs concurrently with httpx.AsyncClient, a 5-second timeout per request, up to 2 retries with exponential backoff on failure, and Pydantic validation of each JSON response — then print a summary of successes, retried-then-succeeded, and hard failures.

Check yourself

1. Why add a timeout to every request instead of letting httpx wait indefinitely for a response?

2. What's the risk of retrying a failed request immediately, with no backoff delay, against a server that's already struggling?

← Previous lesson

Pydantic: validating data

Answer the check-yourself questions to unlock this