Quickstart
Define the State, an Action and a Reducer¶
Httperactor is designed to work with the Unidirectional Data Flow pattern implemented by the pydepot package. For detailed guide on how to set up pydepot, visit its quickstart.
Following example defines a simple state and an action that sets a list of books:
from collections.abc import Sequence
from typing import NamedTuple
import pydepot
class Book(NamedTuple):
title: str
author: str
class State(NamedTuple):
books: tuple[Book, ...]
class SetBooksAction(pydepot.Action):
def __init__(self, books: Sequence[Book]):
self.books: Sequence[Book] = books
class SetBooksReducer(pydepot.Reducer[SetBooksAction, State]):
@property
def action_type(self) -> type[SetBooksAction]:
return SetBooksAction
def apply(self, action: SetBooksAction, state: State) -> State:
return State(books=tuple(action.books))
Define a Request¶
A Request is an object describing an HTTP request. It is also used to transform the response text to a python object.
Following example defines a GET request that fetches a list of books:
import json
import httperactor
...
class GetBooksRequest(httperactor.Request[Sequence[Book]]):
@property
def path(self) -> str:
return "/books"
def map_response(self, response: str) -> Sequence[Book]:
return json.loads(response, object_hook=lambda res: Book(**res))
Create an Interactor¶
An Interactor is an object that "interacts" with some HTTP service. Usually, it is positioned below the layer responsible for capturing the user input.
The HttpInteractor is designed as a Template Method, which makes creating subclasses easy and flexible.
Basic usage consists of implementing the request property, which returns the Request to send, and the actions method, which returns a list of pydepot.Action that will be dispatched to the store.
Following example defines an interactor that will send the GetBooksRequest, and dispatch a single SetBooksAction on successful response:
import httpx
import httperactor
...
class GetBooksInteractor(httperactor.HttpInteractor[httpx.Request, Sequence[Book], State]):
@property
def request(self) -> httperactor.Request[Sequence[Book]]:
return GetBooksRequest()
def actions(self, response: Sequence[Book]) -> Sequence[pydepot.Action]:
return [SetBooksAction(books=response)]
Send the Request¶
Before sending the request, let's create a StoreSubscriber that will print any changes to the State - this will help in veryfing that the interactor is doing its job:
...
class StateSubscriber:
def on_state(self, state: State) -> None:
print(f"[StoreSubscriber] on_state called with {state}")
The endpoint of GetBooksRequest - GET /books - returns the following json document:
$ curl localhost:5000/books
[
{
"author": "Alice",
"title": "foo"
},
{
"author": "Bob",
"title": "bar"
},
{
"author": "Charlie",
"title": "baz"
}
]
Following example sets up the store, creates the interactor and sends a request by awaiting the execute coroutine:
import asyncio
...
async def main() -> None:
store = pydepot.Store(initial_state=State(books=()))
store.register(SetBooksReducer())
subscriber = StateSubscriber()
store.subscribe(subscriber)
interactor = GetBooksInteractor(
http_client=HttpClient(httpx.AsyncClient(base_url="http://localhost:5000")),
store=store,
)
await interactor.execute()
if __name__ == "__main__":
asyncio.run(main())
Running the script will result in the following output:
$ python3 main.py
[StoreSubscriber] on_state called with:
State(
books=(
Book(title='foo', author='Alice'),
Book(title='bar', author='Bob'),
Book(title='baz', author='Charlie')
)
)
Next steps¶
To see more in-depth examples, see the Advanced Usage.
To see all available properties and methods, see the API Documentation.