Getting Started with Realtime¶
Three steps: turn it on, mount the endpoint, and say which models announce their writes.
Step 1: Turn it on¶
Off by default: it costs a LISTEN connection and a pool of consumers per worker holding sockets, which an application with nothing to announce should not pay. Turned on, the services are registered, a worker starts listening with the first socket it takes, and stops with the application. Nothing else to wire in a lifespan.
PostgreSQL only
Announcements travel through LISTEN/NOTIFY, so realtime needs PostgreSQL.
Step 2: Mount the endpoint¶
The socket route is mounted like any other FastEdgy router, on whichever public router the application builds:
from fastapi import APIRouter
from fastedgy.api import realtime
public_router = APIRouter(prefix="/api")
public_router.include_router(realtime.router)
app.include_router(public_router)
It belongs on a public router, not behind an authentication dependency: a browser cannot set an Authorization header on a WebSocket handshake, so the socket authenticates itself with its own first frame.
Step 3: Declare the models that announce¶
Nothing is announced by default. An event nobody is watching for is a NOTIFY, a wake-up on every worker holding that scope and a frame on every socket of it, so a model says so for itself:
from fastedgy.models.base import BaseModel
from fastedgy.models.mixins import WorkspaceableMixin
from fastedgy.orm import fields
from fastedgy.realtime import realtime_model
@realtime_model()
class Company(BaseModel, WorkspaceableMixin):
name = fields.CharField(max_length=200)
domain = fields.CharField(max_length=200, null=True)
Every insert, update and delete of a company is now announced to the clients reading its scope, whoever made it: a browser, an agent through the MCP server, a queued task.
What a client receives¶
Two channels carry each write, and a client subscribes to the one it needs:
| Channel | Who subscribes | What it hears |
|---|---|---|
company | a list of companies | every write of the model |
company:42 | the page reading company 42 | writes on that record |
The frame names what moved:
Only identifiers travel. The client reads the record back through the API, which is what keeps an announcement from carrying a field its reader is not allowed to see.
Step 4: Connect a client¶
In a Vue.js application, one call in the shell holds the socket, and one call per view says what it reads:
import { useRealtime, useResourceChanged } from 'vue-fastedgy';
// Once, in the application shell
useRealtime();
// In a view
useResourceChanged('company', () => reload());
In a Flutter application, flutter_fastedgy holds the socket once asked to, and a holder follows what it holds:
await initializeFastEdgy(realtime: true);
// Whenever the scope being read changes
getService<RealtimeSocket>().watch(scopeSlug);
// A list or a record keeps itself current
final companies = ApiCollection(CompanyApi(), fields: ['id', 'name']);
// Or a listener of its own, cancelled with its screen
final watch = watchResource(CompanyApi(), (event) => reload());
See Vue.js / Realtime for the composables, and Technical Details for the raw protocol if you connect from something else.