agentsclimarketplace

Resonate http service design python

Skill resonatehq/resonate-skills/resonate-http-service-design-python

Agent skills for building with Resonate — durable execution for long-running, crash-safe workflows.

Install
npx -y skills add resonatehq/resonate-skills --skill resonate-http-service-design-python

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 5 stars5 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Design HTTP services in Python that use Resonate durable functions behind FastAPI / Flask / Django route handlers — routing patterns, workflow boundaries, RPC to specialized worker groups, webhook-driven promise resolution. Use when building or refactoring Python HTTP APIs that trigger durable workflows.

The file declares its own license as Apache-2.0. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.

SKILL.md

9.6 KB, as published. Nobody here has run it

Resonate HTTP Service Design — Python

Overview

Route handlers in a Python web framework (FastAPI, Flask, Django) are the entrypoint for durable workflows; they start or await invocations via the Resonate client. Actual business logic runs in worker processes that register durable functions. Downstream services (a DB worker, a search worker) expose their own durable functions and are invoked via RPC.

This skill covers the shape of that split: what runs where, how routes interact with workers, and how webhooks resolve durable promises.

Architecture model

 client → HTTP routes (FastAPI/Flask)
            → Resonate client (r.run / r.rpc / r.promises.resolve)
                → worker group A (business logic)
                    → ctx.rpc → worker group B (specialized, e.g. DB)
  • HTTP server: FastAPI/Flask/Django app handling routes; maps HTTP requests to Resonate invocations
  • Worker service: a process (same or different host) with r.register(...)-ed functions; connects to the Resonate server
  • Resonate server: durable promise store + coordination hub (Rust v0.9.x, single port 8001)

Rules

  • Route handlers are ephemeral. Use Client APIs (r.run, r.rpc, r.promises.*). Never use Context APIs in a route handler.
  • Durable functions run in workers. async def with await ctx.run/rpc + r.register(fn).
  • All external effects (DB, HTTP, filesystem) run inside ctx.run envelopes — never at the top of a durable function, never at module import time.
  • Stable invocation IDs. Derive from request inputs (f"order:{order_id}") — not uuid.uuid4() at route-handler time unless you persist the ID in the response so the client can poll.

Route design patterns

1. Submit-and-poll (async HTTP)

Client submits a job, gets an ID, polls for status:

from __future__ import annotations
import os, time
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from resonate.resonate import Resonate

app = FastAPI()
r = Resonate(url=os.environ.get("RESONATE_URL", "http://localhost:8001"))


class JobSubmission(BaseModel):
    order_id: str
    items: list[str]


@app.post("/jobs")
async def submit_job(body: JobSubmission) -> dict:
    job_id = f"order:{body.order_id}"
    # Fire and forget — workflow runs durably in the background
    r.options(target="order-workers").rpc(job_id, "process_order", body.order_id, body.items)
    return {"job_id": job_id, "status": "started"}


@app.get("/jobs/{job_id}")
async def get_job(job_id: str) -> dict:
    try:
        record = await r.promises.get(job_id)
        state = record.state if hasattr(record, "state") else "pending"
        if state == "resolved":
            return {"job_id": job_id, "status": "done", "result": record.value}
        elif state == "rejected":
            return {"job_id": job_id, "status": "failed", "reason": record.value}
        else:
            return {"job_id": job_id, "status": "running"}
    except Exception:
        raise HTTPException(status_code=404, detail="job not found")

2. Submit-and-callback (webhook resolves promise)

Client submits; workflow blocks on ctx.promise(); external system resolves via webhook:

from resonate.types import Value

# Worker
async def approve_then_ship(ctx, order_id: str) -> dict:
    approval = ctx.promise()
    approval_id = await approval.id()
    await ctx.run(notify_approver, order_id, approval_id)
    decision = await approval
    if not decision.get("approved"):
        return {"status": "rejected"}
    await ctx.run(ship_order, order_id)
    return {"status": "shipped"}


# Route — start the workflow
@app.post("/orders/{order_id}/approve-and-ship")
async def start(order_id: str) -> dict:
    r.options(target="order-workers").rpc(
        f"order:{order_id}", "approve_then_ship", order_id
    )
    return {"status": "pending_approval"}


# Route — webhook resolves the durable promise
@app.post("/webhooks/approval/{order_id}")
async def approval_webhook(order_id: str, payload: dict) -> dict:
    await r.promises.resolve(
        f"approval:{order_id}",
        Value(data={"approved": payload.get("approved", False)}),
    )
    return {"ok": True}

3. Synchronous HTTP wrapping a fast workflow

If the workflow completes quickly, block on it and return the result directly:

@app.post("/price-quote")
async def price_quote(body: QuoteRequest) -> dict:
    quote_id = f"quote:{body.sku}:{body.qty}"
    result = await r.options(target="pricing-workers").rpc(
        quote_id,
        "compute_quote",
        body.sku,
        body.qty,
    ).result()
    return {"quote_id": quote_id, "price": result["price"]}

r.rpc(...) returns a handle; calling .result() and awaiting it blocks the route handler. Keep timeouts tight — a slow worker holds the HTTP request open.

Worker-side lifecycle

A worker process:

  1. Instantiates Resonate(url=..., group=...)
  2. Registers its durable functions
  3. Stays alive (via its own event loop or a while True sleep)
# workers/order_worker.py
from __future__ import annotations
import asyncio, os
from typing import TYPE_CHECKING
from resonate.resonate import Resonate

if TYPE_CHECKING:
    from resonate.context import Context

r = Resonate(url=os.environ.get("RESONATE_URL", "http://localhost:8001"), group="order-workers")

async def process_order(ctx: Context, order_id: str, items: list[str]) -> dict:
    await ctx.run(validate_order, order_id, items)
    await ctx.run(charge_payment, order_id)
    await ctx.run(create_shipment, order_id)
    return {"order_id": order_id, "status": "done"}

r.register(process_order)

if __name__ == "__main__":
    async def main() -> None:
        try:
            # Block until Ctrl-C; SDK long-polls for work
            await asyncio.Event().wait()
        finally:
            await r.stop()

    asyncio.run(main())

Separating worker groups

For larger services, split specialization across worker groups:

# db worker group
r_db = Resonate(url=url, group="db")

async def query_orders(ctx: Context, user_id: str) -> list:
    db = ctx.get_dependency(psycopg.Connection)
    return db.execute("SELECT * FROM orders WHERE user_id = %s", (user_id,)).fetchall()

r_db.register(query_orders)

# business worker group
r_biz = Resonate(url=url, group="business")

async def place_order(ctx: Context, user_id: str, cart: list[str]) -> dict:
    # Reach into the DB group via RPC
    history = await ctx.options(target="db").rpc("query_orders", user_id)
    # ... business logic ...
    return {"user_id": user_id, "order_id": "..."}

r_biz.register(place_order)

Each group runs in its own process (or cluster of processes). RPC routes through Resonate's promise store with the server handling fair dispatch.

Combining FastAPI + in-process worker

Small services often put the HTTP server and the worker in the same process:

from __future__ import annotations
import asyncio, os
from fastapi import FastAPI
from resonate.resonate import Resonate

app = FastAPI()
r = Resonate(url=os.environ.get("RESONATE_URL", "http://localhost:8001"))

async def process_order(ctx, order_id: str) -> dict:
    await ctx.run(charge_payment, order_id)
    return {"order_id": order_id, "status": "done"}

r.register(process_order)

@app.post("/orders/{order_id}")
async def start(order_id: str) -> dict:
    r.run(f"order:{order_id}", process_order, order_id)
    return {"status": "started"}

@app.on_event("shutdown")
async def shutdown() -> None:
    await r.stop()

Because r.register registers in the same process, both RPC-routing and in-process r.run(...) work. For higher throughput, move workers to dedicated processes.

Distinct Python idioms

  • FastAPI is the leading modern web framework; patterns use FastAPI async def route handlers.
  • Pydantic models (BaseModel) for request bodies — idiomatic + validates at parse time.
  • r.run(...) is SYNC — it returns a handle immediately; the actual async work is awaiting handle.result(). You can fire-and-forget by not awaiting the handle.
  • r.promises.* are all async — always await them in async route handlers.
  • Value(data=...) wraps the payload for r.promises.resolve/reject.

Common gotchas

  • Don't use Context APIs in a route handler. ctx only exists inside a r.register()-ed function body. A route handler gets a Resonate client, not a Context.
  • Don't rely on FastAPI request lifecycle for workflow timing. A durable function can outlive an HTTP request by hours or days. Return a job ID and let the client poll.
  • Always await r.stop() on shutdown. Register a shutdown handler (@app.on_event("shutdown")) to drain in-flight work gracefully.
  • Don't forget target. When the route handler calls r.rpc(...), specify .options(target="group-name") so the invocation routes to a worker group.

Related skills

  • resonate-basic-ephemeral-world-usage-pythonResonate, r.run, r.rpc, r.options(target=), promise management
  • resonate-basic-durable-world-usage-pythonr.register, ctx.run, ctx.rpc, ctx.promise
  • resonate-human-in-the-loop-pattern-python — webhook-driven promise resolution (pattern 2 in this skill, expanded)
  • resonate-saga-pattern-python — multi-step workflows suitable for pattern 3 (sync HTTP)

Gives 0 of the 12 instructions most design frontend skills give

Counted across 1,170 of the 1,878 authors here whose files we hold, read 2026-08-06

  • use css variables for color consistencyin 73 of 1170, across 24 files
  • match implementation complexity to the aesthetic visionin 70 of 1170, across 20 files
  • commit to one bold aesthetic direction before codingin 70 of 1170, across 25 files
  • add atmospheric background effects and texturesin 58 of 1170, across 10 files
  • use unexpected spatial compositions and layoutsin 55 of 1170, across 7 files
  • implement real working codein 55 of 1170, across 7 files
  • vary themes and aesthetics across different designsin 48 of 1170, across 7 files
  • launch chromium in headless modein 47 of 1170, across 4 files
  • close the browser when donein 47 of 1170, across 4 files
  • run provided scripts with help flag firstin 47 of 1170, across 4 files
  • use descriptive selectors for elementsin 47 of 1170, across 4 files
  • wait for network idle statein 46 of 1170, across 3 files

Said here and by no other author read

  • run external effects inside ctx.run envelopes
  • derive durable invocation ids from request inputs
  • await r.promises methods in route handlers
  • call await r.stop on shutdown
  • specify target when calling r.rpc in routes
  • keep timeouts tight for synchronous routes

Grouped from the skills themselves: near-identical wordings counted once, and counted by distinct author, so one author publishing three of these counts once.

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.