← Back to blog Article

Going Live: Real-Time Graph Queries in the Portal

In Hantera_, streaming queries are live, self-updating graph queries that keep results current in real time by streaming changes over a websocket, so developers do not need to rewrite queries or bolt on event-reconciliation logic to maintain a live view.

Going Live: Real-Time Graph Queries in the Portal

After what must have been the most ambitious and drawn-out development effort on the platform so far, 2026.3 finally lands the full live query MVP.

Back when the graph was originally designed, I was already entertaining the idea of somehow algorithmically detecting changes inside a graph result. This turned out to be a lot more complicated than what first meets the eye.

The Problem with Classical APIs

Many platforms support events, and Hantera is no exception. But events are easy — and that ease comes with a drawback. Once you get used to working with a data graph, going back to per-entity event streams to understand the state of a system in real time feels like putting on your favorite shirt from when you were 22. It looks good on paper, but it doesn’t quite do the job anymore.

In a graph model, state is never made up of a single node, but a combination of nodes and their edges. And the way you want to combine them depends on which question you’re asking.

A classic REST API might give you its idea of what an order is. For example, an order might carry its order lines. And in the best case, there might be an event that tells you something happened to it, so you know its state has changed.

But what about answering “give me all orders whose customer has an open support ticket”? In a non-modern API, you’d be at the mercy of whether a platform developer foresaw this query and provided the necessary model and filters for it. For events? You’d be stuck listening to order events, customer events, and ticket events, and reconciling everything yourself. That’s error-prone and expensive, and the cost of maintaining such a system can easily skyrocket.

Going Live

So how do you do this in Hantera? Let’s look at an example. First, you model the question as a graph traversal — a plain object:

const spec = {
  edge: 'orders',
  orderBy: 'createdAt desc',
  node: {
    fields: ['orderNumber', 'orderState'],
    navigate: [{
      edge: 'customer',
      require: 'some',
      node: {
        fields: ['customerNumber', 'name'],
        navigate: [{
          edge: 'supportTickets',
          require: 'some',
          filter: "ticketState == 'open'",
          node: { fields: ['ticketNumber', 'ticketState'] },
        }],
      },
    }],
  },
}

The two require: 'some' are doing the heavy lifting. They say: only return an order if its customer has at least one open support ticket. (Assume a supportTickets edge on the customer node — the point is the traversal, not the specific edge.)

To fetch this once, you hand the spec to the Portal Graph API:

const { graph } = useBackendApi()

const orders = await graph.queryGraph<Order>(spec)

Now, if you want this data set to update live, hand the same spec to the Portal’s Live Query API:

const { query } = useLiveQueries()

const { nodes: orders, loading } = query<Order>(spec)

Did you see it? The spec is the same object. You don’t rewrite the query, you don’t add a new endpoint, and you don’t set up any event reconciliation. You just hand the same question to a function that keeps the answer up to date for you.

Under the covers

The portal SDK hides some complexity for us. While the majority of the weight is pulled by the back-end, changes are streamed to clients over a websocket and need to be sorted client-side. If you want to dig into the details — and perhaps implement your own client — check out the protocol specification over in the developer portal.