Modal logo

Polling for a delayed result on Modal

This example shows how you can poll for a delayed result on Modal.

The function factor_number takes a number as input and returns the prime factors of the number. The function could take a long time to run, so we don’t want to wait for the result in the web server. Instead, we return a URL that the client can poll to get the result.

import fastapi
import modal
from modal.functions import FunctionCall
from starlette.responses import HTMLResponse, RedirectResponse

app = modal.App("example-poll-delayed-result")

web_app = fastapi.FastAPI()


@app.function(image=modal.Image.debian_slim().uv_pip_install("primefac"))
def factor_number(number):
    import primefac

    return list(primefac.primefac(number))  # could take a long time


@web_app.get("/")
async def index():
    return HTMLResponse(
        """
    <form method="get" action="/factors">
        Enter a number: <input name="number" />
        <input type="submit" value="Factorize!"/>
    </form>
    """
    )


@web_app.get("/factors")
async def web_submit(request: fastapi.Request, number: int):
    call = factor_number.spawn(
        number
    )  # returns a FunctionCall without waiting for result
    polling_url = request.url.replace(
        path="/result", query=f"function_id={call.object_id}"
    )
    return RedirectResponse(polling_url)


@web_app.get("/result")
async def web_poll(function_id: str):
    function_call = FunctionCall.from_id(function_id)
    try:
        result = function_call.get(timeout=0)
    except TimeoutError:
        result = "not ready"

    return result


@app.function()
@modal.asgi_app()
def fastapi_app():
    return web_app