Skip to content

Flask example

A minimal Flask app that schedules an add_numbers task per request and exposes a second endpoint to poll for the result, using JsonSerializer for task results.

Run it

cd examples/flask_app
uv run --with flask flask run

Then, in another terminal:

curl -X POST localhost:5000/add -H 'Content-Type: application/json' -d '{"a": 2, "b": 3}'
# {"task_id": 1}

curl localhost:5000/result/1
# {"result": 5, "status": "done"}

The worker that executes the task runs in a background thread inside the Flask process, purely to keep this example to a single command. See the plain_python example for the more common pattern of running alchemical_worker as a separate process.

app.py

"""Flask example: schedule a task from a request handler, poll for its
result from another one.

For this mini example the worker runs in a background thread inside the
same process, so `flask run` alone is enough to see it work end to end. In a
real deployment you would instead run `alchemical_worker` as its own
process (see the plain_python example) so it can be scaled independently of
the web app.
"""

import os
import threading
from datetime import timedelta
from typing import Union

from flask import Flask, jsonify, request
from sqlalchemy import create_engine

from alchemical_queues import AlchemicalQueues, JsonSerializer
from alchemical_queues.tasks import TaskInfo, Worker, task


@task
def add_numbers(taskinfo: TaskInfo, a: int, b: int) -> int:
    return a + b


def create_app(database_url: Union[str, None] = None) -> Flask:
    app = Flask(__name__)

    database_url = database_url or os.environ.get(
        "DATABASE_URL", "sqlite:///flask_example.db"
    )
    engine = create_engine(database_url)
    queues = AlchemicalQueues(engine)
    queues.create_all()

    # JsonSerializer for responses: results only ever need to be plain ints
    # here, and JSON is a more portable storage format than pickle.
    task_queue = queues.get_task_queue(
        "add-queue", response_serializer=JsonSerializer()
    )

    worker = Worker(task_queue, poll_every=timedelta(milliseconds=100))
    threading.Thread(target=worker.work, daemon=True).start()

    @app.post("/add")
    def schedule_add():
        payload = request.get_json()
        entry = add_numbers(payload["a"], payload["b"]).schedule(task_queue)
        return jsonify(task_id=entry.entry_id)

    @app.get("/result/<int:task_id>")
    def get_result(task_id: int):
        queued = add_numbers.retrieve(task_queue, task_id)
        if not queued.done:
            return jsonify(status="pending"), 202
        return jsonify(status="done", result=queued.result)

    return app


app = create_app()

if __name__ == "__main__":
    app.run(debug=True)