Every change, in every browser, as it happens.
Your function publishes on a channel and every browser connected to it gets the message at once. You see who is connected, browsers can message each other, and a SQL database's table changes go to channels too. There is no WebSocket server to write or run.
What you get
Channels
A channel is a name your function and the browser agree on, such as room-1. There is nothing to create: the first connection or message brings it to life.
Publish from anywhere
With env.REALTIME.publish from a function, or from the API, the CLI and MCP. Each message reaches every open connection once, in the order it was sent.
Presence
Each browser gets the channel's connected users when it joins, and sees others join and leave as it happens. A function asks with env.REALTIME.presence.
Publish from the browser
With a token that has publish: true, the browser sends messages itself, and everyone else on the channel gets them with the sender in from. For chat, live cursors or typing indicators.
Short-lived tokens
A browser connects only with a token your function makes for one channel and one user, for 10 minutes by default and an hour at most. The signing key is in the project's secrets; rotate it and earlier tokens stop working.
Table changes
Each insert, update and delete in the tables you choose goes to a channel once committed: every change once, in commit order. Switched on per database, and off by default.
One WebSocket, nothing to install
The browser connects to one address with its own WebSocket. Messages are plain JSON with a type: presence, join, leave, message and change.
Channels per environment
Production, previews and development each have their own channels, so a preview's message never reaches production's users.
Calm deploys
On a deploy or a move, a connection closes with code 1012 so the browser reconnects. Adding a server moves no channel.
See your channels
Active channels, connections, the last hour's messages and each channel's connected users in the dashboard, with a test message from there. Daily usage with peak connections.
A chat channel
Your function makes each user a token; the browser connects with it, sees who is connected and sends messages itself.
export default {
async fetch(request, env) {
const { pathname } = new URL(request.url)
if (pathname === "/token") {
const user = await currentUser(request) // your own sign-in
return Response.json(await env.REALTIME.token({ channel: "room-1", user, publish: true }))
}
if (pathname === "/announce" && request.method === "POST") {
const { delivered } = await env.REALTIME.publish("room-1", await request.json())
return Response.json({ delivered })
}
return new Response("not found", { status: 404 })
},
}Table changes, no code
Choose the tables; each committed change arrives on that table's channel with type change, beside the messages your function sends. Updates and deletes need the table to have a primary key.
cloudhost realtime table-changes on shop \
--table orders --table public.items=stock \
--events insert,update --acknowledge-always-onPer message and connected minute
Each message written to a connected browser is one message, and each minute a connection is open is one connected minute. Presence events and pings are not counted. Table changes are billed per hour, like an always-on database. Use comes from the month's credit first, then from the wallet.
Questions
No, a browser gets what is published while it is connected. Keep what must be seen afterwards in your SQL database and load it when the page opens.
A browser cannot connect without a token, and your function makes the token for one channel and one user after checking their sign-in. An expired token, or one signed with another key, is refused.
No. When a browser publishes, the channel itself sets from to the token's user, and any from the browser sends is ignored. Send a message that must be checked through a route of your function.
While changes are being sent, the database does not sleep and stays on, so it is billed like an always-on database. The dashboard and the CLI say so before you switch it on. Switch it off and the database sleeps when idle again.
Connect again, with a new token if the old one has expired. A deploy closes connections with code 1012. To keep a connection open, the browser sends a ping at least every 50 seconds.
Realtime gets a message to everyone on a channel and needs no server code. If the server must check or change each message, or keep state, use Actors.
Open your first channel
One line to publish, one WebSocket in the browser.
Sign-up is by invite for now. If you have a code, create your account.
Sign up withEmail codeSMS code