Skip to content

Queries

The QueryStore class wraps the EventStore and allows you to subscribe to computed state in the form on Queries

The role of the query store is to keep track of all queries, wrap them in the rxjs share operator and ensure only one instance of each is created

IMPORTANT

For performance reasons UI components should only subscribe to the QueryStore and NOT the EventStore

Creating a query store

ts
import { EventStore, QueryStore } from "applesauce-core";

const eventStore = new EventStore();

// the query store takes the upstream event store as the first argument
const queryStore = new QueryStore(eventStore);

Running a query

The queryStore.createQuery can be used to create and register a query in the query store

Queries are not started until the they are subscribe to. and they are destroyed when all subscriptions are closed

ts
// The first argument is the query constructor and the remaining are passed to the query
const observable = queryStore.createQuery(TimelineQuery, [{ kinds: [1] }]);

// start the query by subscribing to it
observable.subscribe((events) => {
  console.log(events);
});

// adding events to the event store will update the timeline query
eventStore.add({kind: 1, content: 'new note', ...})

Performance

The query store keeps track of what queries have been created and will ensure that only a single instance is created

ts
const notes = queryStore.createQuery(TimelineQuery, [{ kinds: [1] }]);

// lots of code...

const otherTimeline = queryStore.createQuery(TimelineQuery, [{ kinds: [1] }]);

// because the query with the same arguments was created before
// the second call will return the same observable
console.log(otherTimeline === notes);

// this will create a new query because the filter is different
const files = queryStore.createQuery(TimelineQuery, [{ kinds: [1063] }]);

Prebuilt queries

Queries are methods that construct complex observable pipelines off of the EventStore

They are run inside the QueryStore which ensures there are not duplicate queries

Custom Queries

A custom query is simply a function that returns a Query object

For example here is a custom query that will subscribe to and parse a NIP-78 app event that contains json

ts
import { map } from "rxjs/operators";

function AppSettingsQuery<T>(pubkey: string): Query<T> {
  return {
    key: pubkey,
    run: (eventStore) => {
      return eventStore.replaceable(30078, pubkey, "app-settings").pipe(
        map((event) => {
          if (!event) return undefined;
          return JSON.parse(event.content) as T;
        }),
      );
    },
  };
}

const sub = queryStore
  .createQuery(AppSettingsQuery, "3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d")
  .subscribe((json) => {
    // json will either be undefined or { theme: string }
    if (json) console.log("updated data", json);
  });

eventStore.add({
  kind: 30078,
  content: '{"theme": "dark"}',
  // rest of event
});