Getting Started¶
This guide walks you through setting up EDRI from scratch and creating your first event-driven application.
Quickstart¶
The shortest possible EDRI application — copy this file, install, and run:
Create main.py:
from datetime import datetime
from edri import EDRI
from edri.api.dataclass.api_event import api, ApiType
from edri.dataclass.event import Event
from edri.dataclass.response import Response, response
from edri.abstract import ManagerBase, request
from http import HTTPMethod
@response
class GreetingResponse(Response):
message: str
timestamp: str
@api(url="/hello/{name}", method=HTTPMethod.GET, exclude={ApiType.HTML, ApiType.WS})
class Greeting(Event):
name: str
response: GreetingResponse
class GreetingManager(ManagerBase):
@request
def greet(self, event: Greeting) -> None:
event.response.message = f"Hello, {event.name}!"
event.response.timestamp = datetime.now().isoformat()
if __name__ == "__main__":
edri = EDRI()
edri.add_component(GreetingManager(edri.router_queue))
edri.start_api()
edri.run()
Install development and run:
pip install "edri[uvicorn]==<VERSION>" && python main.py
In another terminal:
curl -H "Accept: application/json" http://localhost:8080/hello/World
Expected response:
{"data": {"message": "Hello, World!", "timestamp": "2025-06-04T12:00:00"}, "status": "OK"}
Installation¶
Prerequisites¶
Python 3.12+ (3.12, 3.13, 3.14, and pre-release versions are tested in CI)
POSIX-compatible OS (Linux; macOS is experimental — never tested in CI)
Install from PyPI¶
Install the documented version (development) with the recommended ASGI server:
python -m pip install "edri[uvicorn]==<VERSION>"
For the latest stable release, omit the version pin:
python -m pip install "edri[uvicorn]"
Other supported ASGI servers:
Server |
Install command |
Feature |
|---|---|---|
uvicorn |
|
Recommended, fastest |
hypercorn |
|
HTTP/3 support |
daphne |
|
Twisted-based |
Each of these pulls in edri[api], the HTTP/WebSocket layer itself (jinja2,
markdown, multipart). Plain pip install edri installs only the core -- Router,
Managers, Workers, Switch, Scheduler and Store -- and importing anything under
edri.api then raises an ImportError naming edri[api].
The edri command is installed with the optional cli extra, edri[cli]; see
Create a project.
To install with development tools (Sphinx, coverage, twine):
python -m pip install "edri[dev]"
Check installed version¶
python -c "from importlib.metadata import version; print(version('edri'))"
The documented version is development. Use the version switcher in the sidebar to select docs matching your installed version.
Create a project¶
The optional cli extra provides the edri command. edri new creates a project
skeleton with one placeholder Manager:
python -m pip install "edri[cli,uvicorn]==<VERSION>"
edri new myapp
cd myapp
python -m pip install -r requirements.txt
python run.py
In another terminal:
curl -H "Accept: application/json" http://localhost:8080/ping
The generated layout:
myapp/
├── run.py
├── requirements.txt
├── component/
│ └── example/
│ ├── __init__.py
│ └── manager.py
├── event/
│ └── example/
│ ├── __init__.py
│ ├── api/
│ │ ├── __init__.py
│ │ └── ping.py
│ └── core/
│ ├── __init__.py
│ ├── internal.py
│ └── scheduled.py
├── config/
│ ├── __init__.py
│ └── setting.py
└── utility/
└── __init__.py
event/<domain>/api/holds public@apievents, one module per event with its response class;__init__.pyre-exports the events.event/<domain>/core/internal.pyholds events exchanged between Managers;core/scheduled.pyholds events fired by the Scheduler.component/<name>/__init__.pyre-exports the Manager; its logic is inmanager.py.
edri new NAME refuses an existing non-empty directory. --directory DIR creates
the project in DIR instead of ./NAME. --force adds the files to an existing
non-empty directory and never overwrites a file; if any file would be overwritten,
nothing is written.
Shell completion¶
edri completion SHELL prints the completion script for bash, zsh or fish.
Enable it once, for example in ~/.bashrc:
eval "$(edri completion bash)"
Use zsh in ~/.zshrc, or edri completion fish | source in
~/.config/fish/config.fish. Completion offers the subcommands, their options
and paths for --directory. Installing the package does not enable it, because pip
cannot place scripts where a shell looks for them.
Minimal example: REST API¶
Let's build a simple REST endpoint that returns a greeting.
1. Define the event and response¶
Create events/greeting.py:
from http import HTTPMethod
from edri.api.dataclass.api_event import api, ApiType
from edri.dataclass.event import Event
from edri.dataclass.response import Response, response
@response
class GreetingResponse(Response):
message: str
timestamp: str
@api(
url="/hello/{name}",
method=HTTPMethod.GET,
exclude={ApiType.HTML, ApiType.WS},
)
class Greeting(Event):
name: str
response: GreetingResponse
2. Define the Manager¶
Create managers/greeting.py:
from datetime import datetime
from edri.abstract import ManagerBase, request
from events.greeting import Greeting
class GreetingManager(ManagerBase):
@request
def greet(self, event: Greeting) -> None:
event.response.message = f"Hello, {event.name}!"
event.response.timestamp = datetime.now().isoformat()
3. Create the entry point¶
Create main.py:
from edri import EDRI
from managers.greeting import GreetingManager
if __name__ == "__main__":
edri = EDRI()
edri.add_component(GreetingManager(edri.router_queue))
edri.start_api()
edri.run()
4. Run it¶
python main.py
5. Call it¶
curl -H "Accept: application/json" http://localhost:8080/hello/World
Response:
{
"data": {
"message": "Hello, World!",
"timestamp": "2025-06-04T12:00:00"
},
"status": "OK"
}
Understanding what happened¶
sequenceDiagram
participant C as Client (curl)
participant L as Listener
participant B as Broker
participant R as Router
participant M as GreetingManager
C->>L: GET /hello/World (Accept: application/json)
Note over L: parse request · match URL to Greeting<br/>extract name="World" · create Greeting(name="World")
L->>B: event (Broker Queue)
Note over B: run middleware (if any)
B->>R: event (Router Queue)
Note over R: look up subscribers for Greeting
R->>M: deliver to GreetingManager
Note over M: greet() handler sets<br/>response.message = "Hello, World!"<br/>→ auto-sent on return
M->>R: response
Note over R: look up subscribers for the response
R->>B: forward response
Note over B: run response middleware (if any)
B->>L: forward response
Note over L: serialize to JSON
L->>C: HTTP 200
Next steps¶
Now that you have a working EDRI application, explore:
Topic |
Document |
|---|---|
Events in detail (lifecycle, types, inheritance) |
|
API Events (input extraction, URL) |
|
Validation with |
|
Manager patterns |
|
Workers (threads/processes) |
|
Middleware (auth, logging) |
|
HTML API (Jinja2 templates) |
|
WebSocket API |
|
File downloads |
|
Response directives |
Concrete examples¶
Example |
File |
|---|---|
REST CRUD with validation |
|
HTML form with Post/Redirect/Get |
|
CPU-bound WorkerProcess |
|
Streaming ZIP via SharedMemoryPipe |
|
Periodic refresh via Scheduler + Store |
|
WebSocket ticket pattern for async processing |
Asynchronous File Processing with WebSocket Feedback (Ticket Pattern) |