---
term: 'Real-Time Live Queries'
seoTitle: 'What are Live Queries? Real-Time Database Subscriptions'
headline: 'What are Real-Time Live Queries?'
slug: real-time-live-queries
category: api-realtime
shortDefinition: 'A live query is a subscription to a database query — the server pushes create, update, and delete events for matching rows as they happen.'
relatedTerms:
  - websockets-real-time-sync
  - event-driven-architecture
  - push-notifications-apns-fcm
  - database-triggers-beforesave-aftersave
contrastsWith:
  - websockets-real-time-sync
faq:
  - question: 'What is a live query?'
    answer: 'A standing subscription to a database query: instead of asking once and getting a snapshot, you subscribe to the query and the server pushes an event every time its result set changes — a matching record created, updated, deleted, or edited into or out of the results. Your screen stops polling and starts listening.'
  - question: 'How is a live query different from a normal query?'
    answer: 'Lifetime. A normal query runs, returns, and is done — its answer starts aging immediately. A live query stays registered on the server: the initial results arrive the same way, and then targeted change events keep them current until you unsubscribe. Same predicate language, opposite relationship with time.'
  - question: 'How do live queries work under the hood?'
    answer: 'Two halves joined: a change source and a transport. The database emits its stream of writes — via change streams, replication logs, or triggers — and a subscription server matches each change against registered query predicates, pushing relevant events to subscribers over WebSockets. The client SDK wraps the socket lifecycle so your code only sees typed events.'
  - question: 'What are the enter and leave events?'
    answer: 'The subtle pair that makes query subscriptions precise. Enter fires when an existing record is edited so it starts matching your predicate; leave fires when an edit makes it stop matching. A task reassigned to you enters your assigned-to-me subscription without being created; reassigned away, it leaves without being deleted. Create, update, and delete cover the rest.'
  - question: 'Why are live queries better than polling?'
    answer: 'Three ways at once: latency — changes arrive in real time instead of at the next poll; bandwidth — events carry only what changed instead of refetching everything; and load — the server does per-change matching instead of running full queries per client per interval. Polling every few seconds is simulation; subscriptions are the real thing.'
  - question: 'What do live queries add over raw WebSockets?'
    answer: 'The query brain. A raw socket moves messages; everything else — which clients care about which changes, what the events mean, who is allowed to see what, catching up after reconnects — is code you write. A live query layer does predicate matching server-side, types the events, enforces permissions per subscriber, and manages the socket lifecycle. It is the difference between a transport and a feature.'
  - question: 'Are GraphQL subscriptions the same as live queries?'
    answer: 'Same family, different trigger. GraphQL subscriptions fire on named events you define — a mutation publishes, subscribers receive. Live queries fire on result-set changes to a query — no event wiring, the predicate is the subscription. Both ride WebSockets; live queries trade explicit event design for automatic coverage of every write path.'
  - question: 'How do live queries scale?'
    answer: 'On two axes: connections — thousands of open sockets spread across subscription servers behind a pub/sub backplane — and matching — every write checked against registered predicates, which is why selective subscriptions on indexed fields matter. Managed platforms run both axes for you; the client-side discipline is subscribing narrowly and unsubscribing when screens close.'
codeLanguages: [javascript, dart, swift, kotlin]
externalAuthorities:
  - name: 'LiveQuery documentation'
    url: 'https://docs.parseplatform.org/parse-server/guide/#live-queries'
  - name: 'MongoDB change streams documentation'
    url: 'https://www.mongodb.com/docs/manual/changestreams/'
  - name: 'LiveQuery protocol specification'
    url: 'https://github.com/parse-community/parse-server/wiki/Parse-LiveQuery-Protocol-Specification'
  - name: 'Back4app Live Query example'
    url: 'https://www.back4app.com/docs/platform/parse-server-live-query-example'
cta:
  title: 'Subscribe to data, not to plumbing'
  text: 'Back4app Live Queries turn any query into a subscription: five typed events, ACLs enforced per subscriber, and the WebSocket fleet managed by the platform. Real-time features in the time it takes to write the query you already had.'
  linkText: 'Start building free'
  linkUrl: 'https://www.back4app.com/signup'
author: 'Back4app Engineering'
publishedDate: '2026-07-27'
translationKey: real-time-live-queries
---

**A live query is a subscription to a database query — the server pushes create, update, and delete events for matching rows as they happen.** It's the missing tense of database access: normal queries speak in the past ("what matched when I asked"), live queries speak in the continuous present ("what matches, as it changes"). The screen stops asking and starts staying right.

## Key takeaways

| Question | Answer |
| --- | --- |
| What it is | A standing query — subscribe once, receive every change to its results |
| The five events | create · update · delete · **enter** · **leave** |
| Under the hood | Database change stream → predicate matching → WebSocket push |
| vs. polling | Real-time latency, delta-sized payloads, no per-interval query load |
| vs. raw sockets | The query layer: matching, typed events, permissions, reconnects |

## The subscription, in code

The defining move — take the query you already had, and subscribe to it:

**JavaScript:**

```javascript
// JavaScript / Node.js — Back4app JS SDK
// A live query: subscribe to a QUERY and receive its changes as events
const query = new Parse.Query('Task');
query.equalTo('assignee', currentUser);

const sub = await query.subscribe();
sub.on('create', (t) => addRow(t));       // new match appeared
sub.on('update', (t) => refreshRow(t));   // a match changed
sub.on('enter',  (t) => addRow(t));       // edited INTO the result set
sub.on('leave',  (t) => removeRow(t));    // edited OUT of the result set
sub.on('delete', (t) => removeRow(t));    // a match was deleted
```

**Flutter:**

```dart
// Flutter / Dart — Back4app Flutter SDK
// A live query: subscribe to a QUERY and receive its changes as events
final liveQuery = LiveQuery();
final query = QueryBuilder<ParseObject>(ParseObject('Task'))
  ..whereEqualTo('assignee', currentUser);

final sub = await liveQuery.client.subscribe(query);
sub.on(LiveQueryEvent.create, (t) => addRow(t));     // new match
sub.on(LiveQueryEvent.update, (t) => refreshRow(t)); // match changed
sub.on(LiveQueryEvent.enter, (t) => addRow(t));      // edited into the set
sub.on(LiveQueryEvent.leave, (t) => removeRow(t));   // edited out of the set
```

**Swift:**

```swift
// iOS / Swift — Back4app Swift SDK
// A live query: subscribe to a QUERY and receive its changes as events
let query = Task.query("assignee" == currentUser)

let subscription = query.subscribeCallback
subscription?.handleEvent { _, event in
  switch event {
  case .created(let t):  addRow(t)        // new match appeared
  case .updated(let t):  refreshRow(t)    // a match changed
  case .entered(let t):  addRow(t)        // edited INTO the result set
  case .left(let t):     removeRow(t)     // edited OUT of the result set
  case .deleted(let t):  removeRow(t)
  }
}
```

**Kotlin:**

```kotlin
// Android / Kotlin — Back4app Android SDK
// A live query: subscribe to a QUERY and receive its changes as events
val client = ParseLiveQueryClient.Factory.getClient()
val query = ParseQuery.getQuery<ParseObject>("Task")
query.whereEqualTo("assignee", currentUser)

val sub = client.subscribe(query)
sub.handleEvent(SubscriptionHandling.Event.CREATE) { _, t -> addRow(t) }
sub.handleEvent(SubscriptionHandling.Event.UPDATE) { _, t -> refreshRow(t) }
sub.handleEvent(SubscriptionHandling.Event.ENTER)  { _, t -> addRow(t) }
sub.handleEvent(SubscriptionHandling.Event.LEAVE)  { _, t -> removeRow(t) }
```

The event vocabulary deserves its table, because **enter** and **leave** are what make this *query* subscription rather than table notification:

| Event | Fires when… | Example (subscribed to "tasks assigned to me") |
| --- | --- | --- |
| create | A new record matches | A task is created for you |
| update | A matching record changes | Your task's status flips |
| **enter** | An edit makes an existing record match | A task is *reassigned to* you |
| **leave** | An edit makes a match stop matching | Your task is reassigned away |
| delete | A matching record is deleted | The task is removed |

## The pipeline behind the push

```mermaid
flowchart LR
  accTitle: How live queries work end to end
  accDescr: Database writes flow into a change stream; a subscription server matches each change against registered query predicates and pushes typed events over WebSockets to subscribed clients, with permissions checked per subscriber.
  W["Writes<br/>(any client, any API)"] --> DB[("Database")]
  DB --> CS["Change stream<br/>(oplog / WAL / triggers)"]
  CS --> M["Subscription server<br/>predicate matching + ACL check"]
  M -->|"WebSocket push"| C1["Subscriber A"]
  M -->|"WebSocket push"| C2["Subscriber B"]
```

Two halves, cleanly separable: the **change source** — the database's own write feed ([change streams](https://www.mongodb.com/docs/manual/changestreams/) in document stores, replication logs elsewhere) — and the **matching layer**, which checks each change against every registered predicate and pushes to exactly the subscribers whose results changed, permissions enforced per subscriber. The [open LiveQuery protocol](https://github.com/parse-community/parse-server/wiki/Parse-LiveQuery-Protocol-Specification) is a readable reference implementation of the whole shape.

## Live queries vs. polling vs. SSE vs. WebSockets

| Approach | Latency | Payload | Server cost | You build |
| --- | --- | --- | --- | --- |
| Polling | Poll interval | Full refetch each time | Queries × clients × frequency | Timers, diffing |
| Long polling | Near-real-time | Full response per event | Held connections | Fallback plumbing |
| SSE | Real-time | Delta | Streams, one-way | Event design |
| Raw WebSockets | Real-time | Whatever you define | Connections + your fan-out | **Everything** |
| **Live queries** | **Real-time** | **Per-change events** | **Matching + connections (managed)** | **The query** |

The last column is the argument: with [raw sockets](/glossary/websockets-real-time-sync/) you build the message protocol, the routing, the permission checks, and reconnect-catch-up; with live queries the *predicate is the protocol* and the platform owns the rest.

## Common use cases

- **Chat and inboxes** — subscribe to the conversation's messages; arrival is an event, not a refresh.
- **Live dashboards** — orders, metrics, fleets: the query defines the view, events keep it true.
- **Collaborative apps** — shared task boards and documents where five people's edits interleave in seconds.
- **Presence and status** — who's online, what's in progress — enter/leave doing their precise work.
- **Operational screens** — support consoles and admin views that must reflect production *now*, not at last refresh.

## Poll, push, or subscribe? A decision matrix

| Reach for live queries when… | Simpler tools suffice when… |
| --- | --- |
| Users watch shared, changing data | Data changes rarely — poll gently or refetch on focus |
| Changes come from many writers and paths | One writer could just push via SSE |
| The view is naturally a query | The message isn't data-shaped (use sockets directly) |
| Permissions must gate what each viewer sees | Everything is public broadcast |
| You'd rather not own fan-out infrastructure | You already operate a real-time platform |

The client-side discipline that keeps subscriptions healthy, whatever you choose: subscribe *narrowly* (selective predicates on indexed fields), and unsubscribe when the screen closes — standing queries are server state, and screens that leak them accumulate cost invisibly.

## Limitations and trade-offs

- **Matching is a real workload.** Every write is checked against registered predicates; thousands of broad subscriptions on hot classes multiply it — selectivity is the lever.
- **Connections are state.** The socket fleet needs affinity, heartbeats, and fan-out backplanes — managed platforms exist precisely because this is undifferentiated heavy lifting.
- **Reconnects need catch-up.** A client that was offline missed events; robust flows re-run the base query on resubscribe rather than trusting the gap was quiet.
- **Ordering and delivery are at-least-once shaped.** Idempotent event handlers (apply by object ID, not by append) absorb the occasional duplicate gracefully.
- **Not everything wants a subscription.** Rarely-viewed, rarely-changed data is polling territory; live queries earn their cost where eyes and writes are both frequent.

## Live queries on Back4app

Back4app is an open-source Backend-as-a-Service (BaaS) platform that combines a managed database, auto-generated REST and GraphQL APIs, authentication, file storage, and Cloud Code serverless functions. [Live Query](https://www.back4app.com/docs/platform/parse-server-live-query-example) is its real-time layer, and the code tabs above are the entire client story: the same query builder you already use, one subscribe call, five typed events — with ACLs checked per subscriber so real-time never bypasses the permission model, and the WebSocket fleet, change streams, and fan-out running as managed infrastructure. Enable Live Query on a class in the dashboard, subscribe from any SDK, and the continuous present tense is a feature, not a project.
