Everything in Appwrite is realtime_
Rows, files, function executions, sessions, teams, and presence all emit events on the same WebSocket. Subscribe once, get changes in milliseconds instead of polling, and only ever receive what the user can read.
Appwrite events
- Databasesrows
- Storagefiles
- Functionsexecutions
- Authaccount
- Presencepresences
Realtime
- Not just databases
- Every service
- Shared by all subscriptions
- 1 socket
- From write to subscriber
- Milliseconds
- Filtered server-side
- Queries
- Live online status
- Presence
One connection, many subscriptions
Create a Realtime client once and every subscription shares a single WebSocket. Add one with subscribe(), swap its channels or queries with update(), and drop it with unsubscribe(), all without reconnecting.
Realtime client
1 WebSocketEvery service publishes to a channel
Databases, Storage, Functions, Auth, teams, and presence all stream on the same socket. The Channel helper builds the string with a fluent API, so leave an ID blank for a wildcard or append .create() to narrow the stream.
Databases
Channel.tablesdb('main').table('orders').row()
tablesdb.<ID>.tables.<ID>.rows
Channel.tablesdb('main').table('orders').row('8f2a')
tablesdb.<ID>.tables.<ID>.rows.<ID>
Channel.rows()
rows
Storage
Channel.bucket('media').file()
buckets.<ID>.files
Channel.bucket('media').file('invoice')
buckets.<ID>.files.<ID>
Channel.files()
files
Functions
Channel.executions()
executions
Channel.function('checkout')
functions.<ID>
Channel.executions().create()
executions.create
Auth, teams, and presence
Channel.account()
account
Channel.teams()
teams.*
Channel.presences()
presences
Filter events before they reach you
Pass queries when you subscribe and Appwrite filters events on the server, so your callback only runs for updates that match. The methods are the ones you already use for lists, from equal to isNull and or.
Events on the channel
- person: 'paige'match
- person: 'walter'dropped
- person: 'paige'match
Filtered on the server
[Query.equal('person', ['paige'])]
Presence that is durable and live
A presence record carries a user, a free-form status, optional metadata, and an expiry. List presences to see who is here, and subscribe for upsert, update, and delete events in milliseconds.
Who is here
presences- PApaige@example.comediting
- WAwalter@example.comonline
- HAhappy@example.comaway
expiresAt
Slides on heartbeat
A predictable payload on every event
Every message carries the events that triggered it, the channels that can receive it, a server timestamp, and a payload matching the resource response model. Branch on the event names, then apply the payload.
Every message carries
['tablesdb.main.tables.orders.rows.8f2a.update']
['rows', 'tablesdb.main.tables.orders.rows']
{ $id: '8f2a', status: 'paid' }
Permission-aware subscriptions
Subscriptions use the same permissions as the rest of Appwrite, so a user only receives updates for resources they can read. Realtime uses the session you had when you subscribed, so reconnect when it changes.
Row updated
Permission.read(Role.team('acme'))
Tools built for developers and agents_
API-first by design. Use the Console, Realtime, SDKs, CLI, Terraform, MCP, and agent skills on the same project.
Developer and agent experience
One WebSocket, filtered server-side.
import { Client, Realtime, Channel, Query } from 'appwrite';
const client = new Client() .setEndpoint('https://<REGION>.cloud.appwrite.io/v1') .setProject('<PROJECT_ID>');
const realtime = new Realtime(client);
const subscription = await realtime.subscribe( Channel.tablesdb('<DATABASE_ID>').table('orders').row(), (response) => { console.log(response.events, response.payload); }, [Query.equal('status', 'paid')],);Everything is an API
REST, GraphQL, and SDKs for every service. Automate and integrate without console-only workflows.
Console
Manage every service in your project from a unified Console. Optimized keyboard access and a Command Center keep you moving fast.
Realtime
Subscribe to live events and react to changes as they happen.
tablesdb.*.tables.*.rows.*
tablesdb.*.tables.*.rows.*.create
MCP
Connect AI agents to your Appwrite project, APIs, and docs through one remote MCP server.
appwrite
https://mcp.appwrite.ioTerraform
Manage Appwrite infrastructure as code with the official provider.
terraform plan
terraform applyAgent skills
Give AI agents SDK-accurate knowledge of Appwrite services and APIs.
npx skills add appwrite/skillsSDKs
Client and server SDKs for the platforms your team already uses. 15 official SDKs available across client and server runtimes.
CLI
Deploy resources, manage projects, and generate typed SDKs from your terminal.
appwrite login
appwrite deployFAQ_
Realtime is a third protocol for talking to Appwrite, alongside REST and GraphQL. Instead of requesting new data over HTTP, you subscribe once and the server pushes new data to every connected client over a WebSocket as soon as it changes. Subscriptions cover events from all of Appwrite services, not just databases.
Client SDKs use a single WebSocket per Realtime client for all subscriptions. Adding one with subscribe(), replacing its channels or queries with update(), and dropping it with unsubscribe() all apply on the existing socket where supported, so there is no full reconnect. The connection closes when you call realtime.disconnect().
Every service publishes channels: account and sessions, teams and memberships, rows, files, function executions, and presences. The Channel helper class builds the channel string for you with a fluent API, so you can target one row or every row in a table. Leave an ID blank to subscribe with a wildcard, and append .create(), .update(), or .delete() to narrow the stream to a single event type.
Yes. Pass queries as a third parameter when you subscribe and Appwrite filters events server-side, so your callback only runs for updates that match. Realtime supports Query.equal, Query.notEqual, the greater than and less than comparisons, Query.isNull, Query.isNotNull, Query.and, and Query.or.
Every message carries four properties: events (the Appwrite events that triggered the update), channels (the channels that can receive it), timestamp (an ISO 8601 time in UTC from the server), and payload (the same data as the matching response model). Branch on the event names in events to decide how to update your UI.
No. Every subscription is secured by the same permissions system used by rows, files, and presences, so a user only receives updates for resources they have permission to access. Granting read to Role.any() is what makes a resource stream to any client, including visitors who are not signed in.
Realtime authenticates with the session that existed when the subscription was created. If you authenticate after subscribing, that subscription will not receive updates for the new user, so create the session first. When a user signs out and another signs in, call realtime.disconnect() and subscribe again for the new session.
A presence is a short-lived record tied to a user, with a userId, a free-form status, optional metadata, and an expiresAt timestamp. It is durable, so you can list presences at any time, and live, so every change fires upsert, update, and delete events. Keep a record alive with a heartbeat, or use realtime.upsertPresence() so it is removed when the connection closes.
Not today. Realtime subscriptions are a client SDK feature and are not offered for Server SDKs with an API key. Presence records are the exception: they are also a regular HTTP resource, so server code can write them with an API key that has the presences.write scope and clients will see the change live.
Yes. The SDK derives the Realtime endpoint from your Appwrite endpoint, which is wss://<REGION>.cloud.appwrite.io/v1/realtime by default. If you run Appwrite behind a custom proxy and moved the Realtime route, call setEndpointRealtime on the client with your own value.
Start building with Realtime_
Subscribe to your first channel and watch rows, files, executions, and presence updates arrive as they happen.
Explore Appwrite_
Modular backend services that share the same project, permissions model, and console.