DBProvider.getItem()method
Get an item from a collection by its id, or undefined if it doesn't exist.
getItem(collection: Collection<string, II, TT>, id: II): Promise<OptionalItem<II, TT>>
new DBProvider<I, T>()
| Return | |
|---|---|
DBProvider<I, T> | Provider with a fully asynchronous interface for database access. |
Provider with a fully asynchronous interface for database access.
Collection, which carries the name plus identifier and data schemas.Through*Provider.The abstract base class every database backend implements. DBProvider<I, T> defines the typed surface that all call sites use — item reads and writes, queries, and realtime sequences — generic over a Collection so the compiler tracks id and data types automatically.
Concrete backends implement the abstract methods; the base class derives requireItem, countQuery, getFirst, and requireFirst from them. DBProvider implements AsyncDisposable.
Type code that accepts "any database" against DBProvider so an in-memory store, a validated chain, or a SQL backend are all interchangeable:
import type { DBProvider } from "shelving/db"
async function publishPost(provider: DBProvider, id: string) {
await provider.updateItem(POSTS, id, { published: true });
}The method surface covers single items (getItem, requireItem, addItem, setItem, updateItem, deleteItem), queries (getQuery, countQuery, setQuery, updateQuery, deleteQuery, getFirst, requireFirst), realtime (getItemSequence, getQuerySequence — iterate with for await...of), and transactions (transact).
Query writes (setQuery, updateQuery, deleteQuery) have a deliberately weak portable contract: DBProvider itself implements them "two-step" — resolve the matching items with getQuery(), then write each one concurrently through the item methods — and engines override them with a native single call where one exists (SQL's UPDATE … WHERE). Either way they are only guaranteed atomic inside transact().
The derived reads (requireItem, getFirst, requireFirst) and the default countQuery are sugar built on getItem() / getQuery(), so a provider only ever has to implement the core operations — and behaviour added by a wrapping provider (see ThroughDBProvider) automatically applies to them too.
transact() runs a callback as a single atomic transaction — every write made through the provider the callback receives is committed together, or not at all:
await provider.transact(async db => {
const from = await db.requireItem(ACCOUNTS, "alice");
const to = await db.requireItem(ACCOUNTS, "bob");
await db.updateItem(ACCOUNTS, "alice", { balance: from.balance - 100 });
await db.updateItem(ACCOUNTS, "bob", { balance: to.balance + 100 });
});The contract is the weakest guarantee shared by all backends, so transactional code stays portable:
transact() calls inside a transaction — most backends throw UnsupportedError, though some (e.g. MemoryDBProvider) support them scoped to the transaction.CloudflareKVProvider) throw UnsupportedError from transact() itself.Transactions are opt-in per backend — FirestoreProvider (shelving/firebase) implements them, BunPostgresProvider (shelving/bun) runs the callback in a SERIALIZABLE Postgres transaction (retrying contention aborts automatically), and MemoryDBProvider runs the callback against a snapshot clone and replays its recorded writes on success. Wrapping providers built on ThroughDBProvider (ValidationDBProvider, RecordingDBProvider, DebugDBProvider) support them whenever their source does, and keep their own behaviour inside the transaction — e.g. reads and writes in a ValidationDBProvider transaction are still validated, and CacheDBProvider mirrors only a committed transaction's writes into its cache.
const provider = new MemoryDBProvider();
const id = await provider.addItem(users, { name: "Dave" });Get an item from a collection by its id, or undefined if it doesn't exist.
getItem(collection: Collection<string, II, TT>, id: II): Promise<OptionalItem<II, TT>>
Get an item from a collection by its id, or throw if it doesn't exist.
requireItem(collection: Collection<string, II, TT>, id: II): Promise<Item<II, TT>>
Subscribe to live changes for a single item by its id.
getItemSequence(collection: Collection<string, II, TT>, id: II): OptionalItemSequence<II, TT>
Add a new item to a collection and return its generated id.
addItem(collection: Collection<string, II, TT>, data: TT): Promise<II>
Set (insert or overwrite) the data for an item by its id.
setItem(collection: Collection<string, II, TT>, id: II, data: TT): Promise<void>
Apply partial updates to an existing item by its id.
updateItem(collection: Collection<string, II, TT>, id: II, updates: Updates<Item<II, TT>>): Promise<void>
Delete an item from a collection by its id.
deleteItem(collection: Collection<string, II, TT>, id: II): Promise<void>
Count the items in a collection matching an optional query.
countQuery(collection: Collection<string, II, TT>, query?: Query<Item<II, TT>>): Promise<number>
Get the items in a collection matching an optional query.
getQuery(collection: Collection<string, II, TT>, query?: Query<Item<II, TT>>): Promise<Items<II, TT>>
Subscribe to live changes for the result of a query.
getQuerySequence(collection: Collection<string, II, TT>, query?: Query<Item<II, TT>>): ItemsSequence<II, TT>
Set (overwrite) the data for every item matching a query.
setQuery(collection: Collection<string, II, TT>, query: Query<Item<II, TT>>, data: TT): Promise<void>
Apply partial updates to every item matching a query.
updateQuery(collection: Collection<string, II, TT>, query: Query<Item<II, TT>>, updates: Updates<TT>): Promise<void>
Delete every item matching a query.
deleteQuery(collection: Collection<string, II, TT>, query: Query<Item<II, TT>>): Promise<void>
Get the first item matching a query, or undefined if there are none.
getFirst(collection: Collection<string, II, TT>, query: Query<Item<II, TT>>): Promise<OptionalItem<II, TT>>
Get the first item matching a query, or throw if there are none.
requireFirst(collection: Collection<string, II, TT>, query: Query<Item<II, TT>>): Promise<Item<II, TT>>
Run a callback as a single atomic transaction — every write made through the callback's provider is committed together, or not at all.
transact(callback: (provider: DBProvider<I, T>) => Promise<X>): Promise<X>