A collection of functional loading methods to make common event loading patterns easier.
The Address Loader is a specialized loader for fetching Nostr replaceable events by their address (kind, pubkey, and optional identifier). It provides an efficient way to batch and deduplicate requests, cache results, and handle relay hints.
import { createAddressLoader } from "applesauce-loaders/loaders";
import { EventStore } from "applesauce-core";
import { RelayPool } from "applesauce-relay";
const eventStore = new EventStore();
const pool = new RelayPool();
// Create an address loader (do this once at the app level)
const addressLoader = createAddressLoader(pool, {
// Pass all events to the event store to deduplicate them
eventStore,
// Optional configuration options
bufferTime: 1000,
followRelayHints: true,
extraRelays: ["wss://relay.example.com"],
});
// Load a profile (kind 0)
addressLoader({
kind: 0,
pubkey: "3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d",
relays: ["wss://relay.example.com"],
}).subscribe((event) => {
// Handle the loaded event
console.log(event);
});
// Load a contact list (kind 3)
addressLoader({
kind: 3,
pubkey: "3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d",
relays: ["wss://relay.example.com"],
}).subscribe((event) => {
// Handle the loaded event
console.log(event);
});
// Load a parameterized replaceable event
addressLoader({
kind: 30000,
pubkey: "3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d",
identifier: "list of bad people",
relays: ["wss://relay.example.com"],
}).subscribe((event) => {
// Handle the loaded event
console.log(event);
});
The Event Loader is a specialized loader for fetching Nostr events by their IDs. It provides an efficient way to batch and deduplicate requests, cache results, and handle relay hints.
import { createEventLoader } from "applesauce-loaders/loaders";
// Create an event loader (do this once at the app level)
const eventLoader = createEventLoader(pool, {
// Pass all events to the event store to deduplicate them
eventStore,
// Optional configuration options
bufferTime: 1000,
followRelayHints: true,
extraRelays: ["wss://relay.example.com"],
});
// Load an event by ID
eventLoader({
id: "2650f6292166624f45795248edb9ca136c276a3d10a0d8f4efd2b8b23eb2d5fc",
relays: ["wss://relay.example.com"],
}).subscribe((event) => {
// Handle the loaded event
console.log(event);
});
// Load from extra relays
eventLoader({
id: "2650f6292166624f45795248edb9ca136c276a3d10a0d8f4efd2b8b23eb2d5fc",
relays: ["wss://relay.example.com"],
}).subscribe((event) => {
// Handle the loaded event
console.log(event);
});
The Timeline Loader is designed for fetching paginated Nostr events in chronological order. It maintains state between calls, allowing you to efficiently load timeline events in blocks until you reach a specific timestamp or exhaust available events.
import { createTimelineLoader } from "applesauce-loaders/loaders";
// Create a timeline loader
const timelineLoader = createTimelineLoader(
pool,
["wss://relay.example.com"],
{ kinds: [1] }, // Load text notes
{ eventStore },
);
// Initial load - gets the most recent events
timelineLoader().subscribe((event) => {
console.log("Loaded event:", event);
});
// Later, load older events by calling the loader again
// Each call continues from where the previous one left off
timelineLoader().subscribe((event) => {
console.log("Loaded older event:", event);
});
// Load events until a specific timestamp
const oneWeekAgo = Math.floor(Date.now() / 1000) - 7 * 24 * 60 * 60;
timelineLoader(oneWeekAgo).subscribe((event) => {
console.log("Event from last week:", event);
});
All loaders support a cacheRequest
option to load events from a local cache.
import { NostrEvent, Filter } from "nostr-tools";
import { createEventLoader } from "applesauce-loaders/loaders";
// Custom method for loading events from a database
async function cacheRequest(filters: Filter[]): Promise<NostrEvent[]> {
return await cacheDatabase.getEvents(filters);
}
const eventLoader = createEventLoader(pool, {
// Pass all events to the event store to deduplicate them
eventStore,
// Pass a custom cache method
cacheRequest,
// Optional configuration options
bufferTime: 1000,
});
// Because no relays are specified, the event will be loaded from the cache
eventLoader({
id: "2650f6292166624f45795248edb9ca136c276a3d10a0d8f4efd2b8b23eb2d5fc",
}).subscribe((event) => {
// Handle the loaded event
console.log(event);
});
All loaders accept these common configuration options:
bufferTime
: Time interval to buffer requests in ms (default 1000)bufferSize
: Max buffer size (default 200)eventStore
: An event store used to deduplicate eventscacheRequest
: A method used to load events from a local cachefollowRelayHints
: Whether to follow relay hints (default true)lookupRelays
: Fallback lookup relays to check when event can't be foundextraRelays
: An array of relays to always fetch frombufferTime
: Time interval to buffer requests in ms (default 1000)bufferSize
: Max buffer size (default 200)eventStore
: An event store used to deduplicate eventscacheRequest
: A method used to load events from a local cachefollowRelayHints
: Whether to follow relay hints (default true)extraRelays
: An array of relays to always fetch fromlimit
: Maximum number of events to request per filtercache
: A method used to load events from a local cacheeventStore
: An event store to pass all events toAll loaders require a request method for loading Nostr events from relays. You can provide this in multiple ways:
The simplest approach is to pass a RelayPool instance directly:
import { createAddressLoader, createEventLoader } from "applesauce-loaders/loaders";
import { RelayPool } from "applesauce-relay";
const pool = new RelayPool();
const addressLoader = createAddressLoader(pool, { eventStore });
const eventLoader = createEventLoader(pool, { eventStore });
You can also provide a custom request method, such as one from nostr-tools:
import { createEventLoader } from "applesauce-loaders/loaders";
import { SimplePool } from "nostr-tools";
import { Observable } from "rxjs";
const pool = SimplePool();
// Create a custom request function using nostr-tools
function customRequest(relays, filters) {
return new Observable((observer) => {
const sub = pool.subscribeMany(relays, filters, {
onevent: (event) => observer.next(event),
eose: () => observer.complete(),
});
return () => sub.close();
});
}
// Create event loader with custom request
const eventLoader = createEventLoader(customRequest, options);