BatchLoaderAdapter
Interface for batch loading collections from server. Enables performance optimization by coalescing multiple collection requests.
Import
import type { BatchLoaderAdapter } from '@gluonic/client'Interface Definition
Exact definition from implementation:
export type BatchLoaderAdapter = {
fetchCollection: (
t: string,
indexKey: string,
indexVal: string
) => Promise<WireRow[]>
fetchCollections?: (
keys: Array<{ t: string, indexKey: string, indexVal: string }>
) => Promise<Record<string, WireRow[]>>
fetchByIds?: (
t: string,
ids: string[]
) => Promise<WireRow[]>
}Total: 3 methods (1 required, 2 optional). Optional methods enable batch coalescing for better performance.
Methods
fetchCollection()
fetchCollection(
t: string,
indexKey: string,
indexVal: string
): Promise<WireRow[]>Fetch a collection by indexed field (e.g., all tasks for a team).
Parameters:
| Parameter | Type | Description |
|---|---|---|
t | string | Model type name |
indexKey | string | Index field name (e.g., ‘teamId’, ‘authorId’) |
indexVal | string | Index value to match |
Returns: Promise<WireRow[]> - Array of matching rows
Example:
// Fetch all tasks for team 'team-123'
const tasks = await batchLoader.fetchCollection(
'task',
'teamId',
'team-123'
)Server Request:
GET /sync/v1/batch?t=task&indexKey=teamId&indexVal=team-123fetchCollections() (Optional)
fetchCollections?(
keys: Array<{ t: string, indexKey: string, indexVal: string }>
): Promise<Record<string, WireRow[]>>Fetch multiple collections in a single request (performance optimization).
Parameters:
| Parameter | Type | Description |
|---|---|---|
keys | Array<{ t, indexKey, indexVal }> | Array of collection queries |
Returns: Promise<Record<string, WireRow[]>> - Map of query keys to rows
Example:
const results = await batchLoader.fetchCollections([
{ t: 'task', indexKey: 'teamId', indexVal: 'team-1' },
{ t: 'task', indexKey: 'teamId', indexVal: 'team-2' },
{ t: 'issue', indexKey: 'projectId', indexVal: 'proj-1' }
])
// Results keyed by query
results['task:teamId:team-1'] // Task[] for team-1
results['task:teamId:team-2'] // Task[] for team-2
results['issue:projectId:proj-1'] // Issue[] for proj-1Performance: Reduces 3 requests to 1.
fetchByIds() (Optional)
fetchByIds?(
t: string,
ids: string[]
): Promise<WireRow[]>Fetch specific models by IDs in a single request.
Parameters:
| Parameter | Type | Description |
|---|---|---|
t | string | Model type name |
ids | string[] | Array of model IDs |
Returns: Promise<WireRow[]> - Array of rows (missing IDs omitted)
Example:
const tasks = await batchLoader.fetchByIds(
'task',
['id-1', 'id-2', 'id-3']
)Performance: More efficient than fetching IDs individually.
Default Implementation
Default HTTP-based batch loader:
const defaultBatchLoader: BatchLoaderAdapter = {
async fetchCollection(t, indexKey, indexVal) {
const params = new URLSearchParams({ t, indexKey, indexVal })
const response = await fetch(`/sync/v1/batch?${params}`)
if (!response.ok) {
throw new Error(`Batch load failed: ${response.statusText}`)
}
const data = await response.json()
return data.rows || []
},
async fetchByIds(t, ids) {
const response = await fetch('/sync/v1/batch', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ t, ids })
})
const data = await response.json()
return data.rows || []
}
}Batching & Coalescing
When fetchCollections is implemented, Gluonic automatically coalesces requests:
// Component renders 50 teams
teams.forEach(team => {
console.log(team.tasks.length) // Triggers lazy load
})
// Without fetchCollections: 50 individual requests
// fetchCollection('task', 'teamId', 'team-1')
// fetchCollection('task', 'teamId', 'team-2')
// ... 48 more
// With fetchCollections: 1 batched request after ~50ms
// fetchCollections([
// { t: 'task', indexKey: 'teamId', indexVal: 'team-1' },
// { t: 'task', indexKey: 'teamId', indexVal: 'team-2' },
// ... all 50
// ])Performance: Reduces 50 requests to 1, saves ~5 seconds.
Custom Implementation
GraphQL Example
import type { BatchLoaderAdapter } from '@gluonic/client'
class GraphQLBatchLoader implements BatchLoaderAdapter {
async fetchCollection(t, indexKey, indexVal) {
const query = `
query FetchCollection($type: String!, $key: String!, $val: String!) {
collection(type: $type, indexKey: $key, indexVal: $val) {
t
id
v
p
}
}
`
const response = await fetch('/graphql', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
query,
variables: { type: t, key: indexKey, val: indexVal }
})
})
const result = await response.json()
return result.data.collection
}
async fetchCollections(keys) {
// Batch query for all collections
const queries = keys.map((key, i) => `
coll${i}: collection(
type: "${key.t}",
indexKey: "${key.indexKey}",
indexVal: "${key.indexVal}"
) { t id v p }
`).join('\n')
const response = await fetch('/graphql', {
method: 'POST',
body: JSON.stringify({ query: `{ ${queries} }` })
})
const result = await response.json()
// Convert to expected format
const output: Record<string, WireRow[]> = {}
keys.forEach((key, i) => {
const queryKey = `${key.t}:${key.indexKey}:${key.indexVal}`
output[queryKey] = result.data[`coll${i}`]
})
return output
}
}
// Use in SyncClient
const client = SyncClient({
server: 'https://api.example.com',
storage,
models,
batch: new GraphQLBatchLoader()
})Usage in Gluonic
BatchLoaderAdapter is used by:
- LazyCollection hydration - Load collection items
- ensureRowsByIds() - Load specific models
- Relationship navigation - Auto-load related data
// User accesses collection
const tasks = team.tasks.elements
// Internally triggers:
// 1. Check storage first
// 2. If not found, call batchLoader.fetchCollection('task', 'teamId', team.id)
// 3. Cache result in storage
// 4. Update poolPerformance Benefits
Without Batch Loader
// Each lazy collection triggers separate request
team1.tasks.length // Request 1
team2.tasks.length // Request 2
team3.tasks.length // Request 3
// 50 teams = 50 requests 😔With fetchCollections
// Coalesced into single batch
team1.tasks.length
team2.tasks.length
team3.tasks.length
// 50 teams = 1 request after 50ms ✓Network Savings: 50x reduction in requests
Configuration
const client = SyncClient({
server: 'https://api.example.com/sync/v1',
storage,
models,
batch: {
fetchCollection: async (t, key, val) => {
// Custom implementation
},
fetchCollections: async (keys) => {
// Optional: batch optimization
},
fetchByIds: async (t, ids) => {
// Optional: ID batch loading
}
}
})See Also
- WireRow - Row format returned
- Store.ensureRowsByIds() - Uses batch loader
- LazyCollection - Uses batch loader for hydration
- Batch Loader Concept - Conceptual overview