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

python -m pip install "edri[uvicorn]"

Recommended, fastest

hypercorn

python -m pip install "edri[hypercorn]"

HTTP/3 support

daphne

python -m pip install "edri[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.

Install from source (authorized contributors)

Source installation is available to authorized contributors. Access instructions are maintained internally by the project maintainers.


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 @api events, one module per event with its response class; __init__.py re-exports the events.

  • event/<domain>/core/internal.py holds events exchanged between Managers; core/scheduled.py holds events fired by the Scheduler.

  • component/<name>/__init__.py re-exports the Manager; its logic is in manager.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)

Event

API Events (input extraction, URL)

API Event

Validation with inject(...)

Validation & Injection

Manager patterns

Manager

Workers (threads/processes)

Worker

Middleware (auth, logging)

Middleware

HTML API (Jinja2 templates)

HTML API

WebSocket API

WebSocket

File downloads

Downloading files

Response directives

Response

Concrete examples

Example

File

REST CRUD with validation

REST CRUD with validation

HTML form with Post/Redirect/Get

HTML form: GET the form, POST it, redirect on success

CPU-bound WorkerProcess

Offloading CPU-bound work to a WorkerProcess

Streaming ZIP via SharedMemoryPipe

SharedMemoryPipe: streaming a ZIP of a folder

Periodic refresh via Scheduler + Store

Periodic refresh into the Key-Value Store

WebSocket ticket pattern for async processing

Asynchronous File Processing with WebSocket Feedback (Ticket Pattern)