Queries
Queries let you sort, filter, and limit the data returned from a reference. All query methods are chainable and return a new query — they don’t modify the original reference.
Ordering
Every query starts with an ordering method. You can only use one orderBy per query.
orderByChild(path)
Sort by a child key’s value:
import { LarkDatabase } from "@lark-sh/client";
const db = new LarkDatabase("my-project/my-database");
await db.connect({ anonymous: true });
// Sort players by score
const topPlayers = db.ref("players").orderByChild("score");
You can also order by nested child paths:
// Sort by a nested field
const byCity = db.ref("users").orderByChild("address/city");
orderByKey()
Sort by each child’s key (alphabetically):
const alphabetical = db.ref("users").orderByKey();
orderByValue()
Sort by each child’s value directly. Useful when children are primitives (numbers, strings) rather than objects:
const byScore = db.ref("highscores").orderByValue();
orderByPriority()
Sort by the priority set on each child node:
const byPriority = db.ref("tasks").orderByPriority();
Limiting results
limitToFirst(count)
Returns only the first count items in the ordered result:
// Get the 10 lowest scores
const bottom10 = db.ref("players")
.orderByChild("score")
.limitToFirst(10);
limitToLast(count)
Returns only the last count items in the ordered result:
// Get the 10 highest scores
const top10 = db.ref("players")
.orderByChild("score")
.limitToLast(10);
Range filters
Range methods narrow results to a specific window. They work with the active ordering.
startAt(value, key?)
Include items with a value greater than or equal to the specified value:
// Players with score >= 100
const eliteRef = db.ref("players")
.orderByChild("score")
.startAt(100);
startAfter(value, key?)
Include items with a value strictly greater than the specified value:
// Players with score > 100
const aboveRef = db.ref("players")
.orderByChild("score")
.startAfter(100);
endAt(value, key?)
Include items with a value less than or equal to the specified value:
// Players with score <= 50
const belowRef = db.ref("players")
.orderByChild("score")
.endAt(50);
endBefore(value, key?)
Include items with a value strictly less than the specified value:
// Players with score < 50
const underRef = db.ref("players")
.orderByChild("score")
.endBefore(50);
equalTo(value, key?)
Match items with exactly the specified value:
// Players with score of exactly 100
const exact = db.ref("players")
.orderByChild("score")
.equalTo(100);
The optional key parameter in range methods is used to disambiguate when multiple children have the same value. It acts as a secondary sort by key.
Query identifier
Each query has a queryIdentifier property — a string that uniquely identifies the combination of ordering, limits, and ranges. This is useful for deduplication or caching:
const query = db.ref("players")
.orderByChild("score")
.limitToLast(10);
console.log(query.queryIdentifier);
// Unique string representing this exact query configuration
Using queries with subscriptions
Queries work with both once() and on(). This is where they really shine — you can subscribe to a filtered, sorted slice of your data in real time.
// Subscribe to top 10 players, updated live
const unsubscribe = db.ref("players")
.orderByChild("score")
.limitToLast(10)
.on("child_added", (snapshot) => {
console.log(`${snapshot.val().name}: ${snapshot.val().score}`);
});
Examples
Leaderboard
Display a live top-10 leaderboard sorted by score:
const leaderboard: Array<{ name: string; score: number }> = [];
const unsubscribe = db.ref("players")
.orderByChild("score")
.limitToLast(10)
.on("value", (snapshot) => {
leaderboard.length = 0;
snapshot.forEach((childSnap) => {
leaderboard.push({
name: childSnap.val().name,
score: childSnap.val().score,
});
});
// Reverse because limitToLast returns ascending order
leaderboard.reverse();
console.log("Top 10:", leaderboard);
});
Load data page by page using startAfter() and limitToFirst():
async function loadPage(
ref: ReturnType<LarkDatabase["ref"]>,
pageSize: number,
lastKey?: string
) {
let query = ref.orderByKey().limitToFirst(pageSize);
if (lastKey) {
query = query.startAfter(lastKey);
}
const snapshot = await query.once("value");
const items: Array<{ key: string; value: any }> = [];
snapshot.forEach((child) => {
items.push({ key: child.key, value: child.val() });
});
return items;
}
// Load first page
const page1 = await loadPage(db.ref("products"), 20);
// Load next page, starting after the last key from page 1
const lastKey = page1[page1.length - 1]?.key;
const page2 = await loadPage(db.ref("products"), 20, lastKey);
You can only use one orderBy method per query. Calling a second orderBy will throw an error. If you need to filter on multiple fields, restructure your data so a single ordering covers your use case, or filter client-side after fetching.