Skip to content

AsyncDataAdapter ​

ts
import { AsyncDataAdapter } from '@signaldb/core'

Answers every query by going to the storage adapter, without holding the collection in memory.

Use it when the data does not fit in memory, or when something other than this process can change the underlying storage and you need the answer to reflect that.

Usage ​

js
import { Collection, AsyncDataAdapter } from '@signaldb/core'
import createFilesystemAdapter from '@signaldb/fs'

const dataAdapter = new AsyncDataAdapter({
  storage: name => createFilesystemAdapter(`./data/${name}`),
})

const Posts = new Collection('posts', dataAdapter)

Options ​

ts
new AsyncDataAdapter(options: {
  storage: (name: string) => StorageAdapter<any, any>
  id?: string
  onError?: (error: Error) => void
  retry?: {
    attempts?: number
    delay?: (attempt: number) => number
  }
})
  • storage: Called once per collection with the collection's name, and returns the storage adapter it reads from. Required — an async adapter with nothing to read from has nothing to do.
  • id: A logical name, handy when several adapters run side by side.
  • onError: Called with errors the adapter cannot hand to a caller: a storage adapter that fails to set up, and a live query that still fails after all retry attempts (as a QueryError naming the collection, selector and attempts; the query is also reported through the collection's query.error event). Defaults to console.error.
  • retry.attempts: Total attempts for a failing query, including the first. Default 3.
  • retry.delay: Delay in milliseconds before attempt N+1. Default 100 * 4 ** (attempt - 1).

Queries are asynchronous ​

A query is not answered the moment you ask for it, which changes two things.

Until it is answered, the cursor returns its neutral result — an empty list, a count of zero — and that is indistinguishable from a query that legitimately matched nothing. Use Cursor#isLoading() to tell the two apart:

js
const cursor = Posts.find({ status: 'published' })

effect(() => {
  if (cursor.isLoading()) return renderSpinner()
  renderPosts(cursor.fetch())
})

And a query can fail. That is what retry is for: a query that fails is otherwise a dead end for the rest of the session, because cursors only requery on completion and would keep serving that neutral empty value. After the attempts are used up the query is published as failed, which reaches the collection's query.error event.

Indices ​

Only the fields a collection declares as indices can narrow a query down before it reaches storage. This matters more here than with the DefaultDataAdapter: an unindexed selector means reading the whole collection out of storage on every query, not just scanning an array already in memory.

Released under the MIT License.