Live Queries

Build collaborative, multiplayer applications faster and easier than ever with Grafbase.

The easiest way to use Live Queries is by using an existing GraphQL client with one of our plugins, or by using the native EventSource API.

Grafbase uses server-sent events to enable Live Queries.

Instead of subscribing to all of the data that changes, the event payload sends a patch with instructions how to get from the previous execution result to the next.

Consider the following schema:

type Post @model {
  title: String!
  slug: String! @unique
}

We could stream events for the collection query, fetching by ID or unique field:

query @live {
  postCollection(first: 10) {
    edges {
      node {
        id
        title
      }
    }
  }
}

Server-Sent Events (SSE) allows the server to push data to the client via HTTP — perfect for streaming changes to queried data.

Since data flows from the server to the client, you should use a GraphQL mutation to send events back to your Grafbase backend.

You should use the EventSource interface on the frontend to handle events from the event stream.

You must configure your GraphQL client to switch the protocol based on whether the @live is used in the request — we have integrations to handle that automatically.

Events sent to the event stream will be sent as JSON Patch, and includes the current revision index to help identity any missed events if you close and reconnect the connection:

{
  "patch": [
    {
      "op": "add",
      "path": "/postCollection/edges/3",
      "value": {
        "node": {
          "id": "post_01GJMDWJ2M6WWTM26S7C1KKYBE",
          "title": "Instant serverless GraphQL backends"
        }
      }
    }
  ],
  "revision": 1
}

Grafbase automatically handles the EventSource connection with these client libraries:

URQL users can install the @grafbase/urql-exchange package to handle @live queries automatically:

npm install urql graphql @grafbase/urql-exchange
import { sseExchange } from '@grafbase/url-exchange'
import { cacheExchange, createClient, dedupExchange, fetchExchange } from 'urql'

// Token generated by your auth provider: https://grafbase.com/docs/auth/providers
const token = '...'

export const client = createClient({
  url: process.env.GRAFBASE_API_URL,
  fetchOptions: {
    headers: {
      authorization: `Bearer ${token}`
    }
  },
  // Make sure `sseExchange` is put before `fetchExchange`
  exchanges: [dedupExchange, cacheExchange, sseExchange, fetchExchange]
})

The sseExchange library will automatically detect the use of @live in the query and send requests to your backend in the correct format:

import { gql } from 'urql'

const query = gql`
  query @live {
    todoListCollection(first: 5) {
      edges {
        node {
          id
          title
        }
      }
    }
  }
`

Apollo Client users can install the @grafbase/apollo-link package to handle @live queries automatically:

npm install @apollo/client graphql @grafbase/apollo-link
import {
  ApolloClient,
  ApolloLink,
  HttpLink,
  InMemoryCache,
  split
} from '@apollo/client'
import { SSELink, isLiveQuery } from '@grafbase/apollo-link'
import { getOperationAST } from 'graphql'

// Token generated by your auth provider: https://grafbase.com/docs/auth/providers
const token = '....'

const initializeApolloClient = (link: ApolloLink) => {
  return new ApolloClient({
    cache: new InMemoryCache(),
    link: link
  })
}

export const createApolloLink = () => {
  const sseLink = new SSELink({
    uri: process.env.GRAFBASE_API_URL,
    headers: {
      authorization: `Bearer ${token}`
    }
  })

  const httpLink = new HttpLink({
    uri: process.env.GRAFBASE_API_URL,
    headers: {
      authorization: `Bearer ${token}`
    }
  })

  return split(
    ({ query, operationName, variables }) =>
      isLiveQuery(getOperationAST(query, operationName), variables),
    sseLink,
    httpLink
  )
}

const client = initializeApolloClient(createApolloLink())

The client instance will automatically detect the use of @live in the query and send requests to your backend in the correct format:

import { gql } from '@apollo/client'

const query = gql`
  query @live {
    todoListCollection(first: 5) {
      edges {
        node {
          id
          title
        }
      }
    }
  }
`

Houdini GraphQL users can install the @grafbase/houdini package to handle @live queries automatically:

npm install @grafbase/houdini
import { PUBLIC_GRAFBASE_API_URL } from '$env/static/public'
import { HoudiniClient } from '$houdini'

// Token generated by your auth provider: https://grafbase.com/docs/auth/providers
const token = '...'

export default new HoudiniClient({
  url: PUBLIC_GRAFBASE_API_URL,
  fetchParams() {
    return {
      headers: {
        authorization: `Bearer ${token}`
      }
    }
  }
})

Houdini will then automatically detect the use of @live in the query and send requests to your backend in the correct format.

query GetAllMessages @live {
  messageCollection(first: 100) {
    edges {
      cursor
      node {
        id
        author
        message
        createdAt
      }
    }
  }
}

Should you use the native EventSource to handle events you use URLSearchParams to send requests with the HTTP header accept: text/event-stream:

const url = new URL('GRAFBASE_API_URL')

// Token generated by your auth provider: https://grafbase.com/docs/auth/providers
const token = '...'

const params = new URLSearchParams({
  authorization: `Bearer ${token}`,
  query: `query @live {
    postCollection(first: 10) {
      edges {
        node {
          id
          title
        }
      }
    }
  }`,
  variables: JSON.stringify({})
})

url.search = params.toString()

const eventSource = new EventSource(url)

eventSource.open = () => {
  console.log('New connection')
}

eventSource.onmessage = message => {
  console.log('New message', message)
}

eventSource.onerror = error => {
  console.error(error)
}
  • You must only use the EventSource with the @live directive.
  • The request must be sent with the HTTP header accept: text/event-streamEventSource does this automatically.
  • All @live queries must use URL params to send the query, variables, API key or authorization token.
  • You must handle reconnecting to the event stream should the connection close. The URQL and Apollo integrations handle this automatically.
Was this page helpful?