Reliable HTTP Requests

Let's learn how to make a webhook-style durable request with Python.

A typical web service action is sending a notification request to a different server. Also known as webhook, this technique often requires thinking through all various failure modes and implementing appropriate mitigation.

What if the server is down? What if it's slow? As developers, we usually start implementing retry loops. What if the server is overwhelmed? maybe let's add exponential back-off. What if our program is a web server that needs to respond quickly? let's add a queue. The complexity of the code and infrastructure to handle this seemingly simple task grows when reliability is considered.

Let's see how to use Dispatch Functions to do all this with just a few lines of code.

Setup

If you haven't already, make a Dispatch account and create an API key. Follow Getting Started if you need assistance getting any of those.

Code Snippet

First, the Dispatch API key needs to be available to the program. This step is highly dependent on your setup and your production infrastructure. In our case, let's use an environment variable to store it:

$ export DISPATCH_API_KEY=<the key you got during setup>

We will also set an environment variable for the destination URL where to send the example requests to:

$ export DISPATCH_ENDPOINT_URL=<the URL that your application is reachable at>

Finally, we export the verification key so the application can validate the signature of requests it receives and guarantee that they originate from the Dispatch platform:

$ export DISPATCH_VERIFICATION_KEY=<the verification key you got during setup>

This example assumes Python 3.11+. As most developers do, let's use the popular requests package to make HTTP requests, and FastAPI to setup the server:

pip install dispatch-functions requests fastapi[all]

Here's the full Python code of this program:

from fastapi import FastAPI
from dispatch.fastapi import Dispatch
import requests

app = FastAPI()
dispatch = Dispatch(app)

@dispatch.function
def publish(url):
    r = requests.post(url, data={'answer': 42})
    r.raise_for_status()

@app.get('/')
def root():
    publish.dispatch('https://httpstat.us/500')
    return 'OK'

Save this code in a main.py file, and we can run the application with:

uvicorn main:app

Dispatch of function runs happens when the server receives a GET request on /. Notice that we are publishing to a URL that will always return a 500. Since internal server errors are interpreted as transient failures, the Dispatch platform automatically retries the operation.

$ uvicorn main:app
INFO:     Started server process [38707]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO:     127.0.0.1:52719 - "GET / HTTP/1.1" 200 OK

@dispatch.function: 'publish' raised an exception
Traceback (most recent call last):
  File "/Users/achilleroussel/go/src/github.com/stealthrocket/dispatch-py/src/dispatch/function.py", line 194, in primitive_func
    raw_output = func(*args, **kwargs)
                 ^^^^^^^^^^^^^^^^^^^^^
  File "/Users/achilleroussel/go/src/github.com/stealthrocket/dispatch-py/main.py", line 11, in publish
    r.raise_for_status()
  File "/opt/homebrew/lib/python3.11/site-packages/requests/models.py", line 1021, in raise_for_status
    raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 500 Server Error: Internal Server Error for url: https://httpstat.us/500
INFO:     127.0.0.1:52725 - "POST /dispatch.sdk.v1.FunctionService/Run HTTP/1.1" 200 OK

@dispatch.function: 'publish' raised an exception
Traceback (most recent call last):
  File "/Users/achilleroussel/go/src/github.com/stealthrocket/dispatch-py/src/dispatch/function.py", line 194, in primitive_func
    raw_output = func(*args, **kwargs)
                 ^^^^^^^^^^^^^^^^^^^^^
  File "/Users/achilleroussel/go/src/github.com/stealthrocket/dispatch-py/main.py", line 11, in publish
    r.raise_for_status()
  File "/opt/homebrew/lib/python3.11/site-packages/requests/models.py", line 1021, in raise_for_status
    raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 500 Server Error: Internal Server Error for url: https://httpstat.us/500
INFO:     127.0.0.1:52725 - "POST /dispatch.sdk.v1.FunctionService/Run HTTP/1.1" 200 OK

This short program shows how we can create reliable applications with just a few lines of code using the Dispatch SDK for Python. We haven't had to add extra infrastructure blocks like queues, databases, or worker pools. Integration with the Dispatch platform is entirely driven from the application code, with very simple constructs.

Last updated

©️ Stealth Rocket, Inc. All rights reserved.