Important: This documentation covers Yarn 1 (Classic).
For Yarn 2+ docs and migration guide, see yarnpkg.com.

Package detail

nostr-fetch

jiftechnify2.9kMIT0.17.0TypeScript support: included

A utility library that allows JS/TS apps to effortlessly fetch past events from Nostr relays

nostr

readme

nostr-fetch

A utility library that allows JS/TS apps to effortlessly fetch past events from Nostr relays.

Installation

for npm Project

npm install nostr-fetch

for Deno Project

deno add npm:nostr-fetch

for Browser Apps, without Bundlers

You can also use nostr-fetch in your HTML via <script> tags, thanks to jsDelivr.

<script type="module">
  import { NostrFetcher } from "https://cdn.jsdelivr.net/npm/nostr-fetch@0.16.0/+esm"
  // ...
</script>

Note for Users on Node.js < v22

Node.js < v22 doesn't have native WebSocket implementation. On Such a environment you may want to pass a custom WebSocket constructor from an external package like ws to NostrFetcher.init() as an option.

npm install ws
import { NostrFetcher } from "nostr-fetch";
import WebSocket from "ws";

const fetcher = NostrFetcher.init({ webSocketConstructor: WebSocket });

Usage

Basics

import { NostrFetcher } from "nostr-fetch";

const nHoursAgo = (hrs: number): number =>
  Math.floor((Date.now() - hrs * 60 * 60 * 1000) / 1000);

const fetcher = NostrFetcher.init();
const relayUrls = [/* relay URLs */];

// fetches all text events since 24 hr ago in streaming manner
const postIter = fetcher.allEventsIterator(
    relayUrls, 
    /* filter (kinds, authors, ids, tags) */
    { kinds: [ 1 ] },
    /* time range filter (since, until) */
    { since: nHoursAgo(24) },
    /* fetch options (optional) */
    { skipFilterMatching: true }
);
for await (const ev of postIter) {
    console.log(ev.content);
}

// fetches all text events since 24 hr ago, as a single array
const allPosts = await fetcher.fetchAllEvents(
    relayUrls,
    /* filter */
    { kinds: [ 1 ] },
    /* time range filter */
    { since: nHoursAgo(24) },
    /* fetch options (optional) */
    { sort: true }
)

Various Fetch Methods

import { NostrFetcher } from "nostr-fetch";

const fetcher = NostrFetcher.init();
const relayUrls = [/* relay URLs */];

// fetches latest 100 text posts
// internally: 
// 1. fetch latest 100 events from each relay
// 2. merge lists of events
// 3. take latest 100 events
const latestPosts: NostrEvent[] = await fetcher.fetchLatestEvents(
    relayUrls,
    /* filter */
    { kinds: [ 1 ] },
    /* number of events to fetch */
    100,
);

// fetches the last metadata event published by pubkey "deadbeef..."
// internally:
// 1. fetch the last event from each relay
// 2. take the latest one
const lastMetadata: NostrEvent | undefined = await fetcher.fetchLastEvent(
    relayUrls,
    /* filter */
    { kinds: [ 0 ], authors: [ "deadbeef..." ] },
);

// fetches latest 10 text posts from each author in `authors`
const postsPerAuthor = fetcher.fetchLatestEventsPerAuthor(
    /* authors and relay set */
    // you can also pass a `Map` which has mappings from authors (pubkey) to reley sets,
    // to specify a relay set for each author
    { 
        authors: ["deadbeef...", "abcdef01...", ...],
        relayUrls,
    },
    /* filter */
    { kinds: [ 1 ] },
    /* number of events to fetch for each author */
    10,
);
for await (const { author, events } of postsPerAuthor) {
    console.log(`posts from ${author}:`);
    for (const ev of events) {
        console.log(ev.content);
    }
}

// fetches the last metadata event from each author in `authors`
const metadataPerAuthor = fetcher.fetchLastEventPerAuthor(
    /* authors and relay set */
    // you can also pass a `Map` which has mappings from authors (pubkey) to reley sets,
    // to specify a relay set for each author
    {
        authors: ["deadbeef...", "abcdef01...", ...],
        relayUrls,
    }
    /* filter */
    { kinds: [ 0 ] },
);
for await (const { author, event } of metadataPerAuthor ) {
    console.log(`${author}: ${event?.content ?? "not found"}`);
}

Working with custom relay pool implementations

First, install the adapter package for the relay pool implementation you want to use. For example, if you want to use nostr-fetch with nostr-tools' SimplePool :

npm install @nostr-fetch/adapter-nostr-tools

Then, wrap your relay pool instance with the adapter and pass it to the initializer NostrFetcher.withCustomPool().

import { NostrFetcher } from "nostr-fetch";
import { simplePoolAdapter } from "@nostr-fetch/adapter-nostr-tools";
import { SimplePool } from "nostr-tools";

const pool = new SimplePool();

// wrap SimplePool with simplePoolAdapter to make it interoperable with nostr-fetch
const fetcher = NostrFetcher.withCustomPool(simplePoolAdapter(pool));

// now, you can use any fetch methods described above!

Table of Available Adapters

Package Relay Pool Impl. Adapter Package Adapter
nostr-tools (v1) SimplePool @nostr-fetch/adapter-nostr-tools simplePoolAdapter
nostr-tools (v2) SimplePool @nostr-fetch/adapter-nostr-tools-v2 simplePoolAdapter
nostr-relaypool RelayPool @nostr-fetch/adapter-nostr-relaypool relayPoolAdapter
@nostr-dev-kit/ndk NDK @nostr-fetch/adapter-ndk ndkAdapter
rx-nostr (v1) RxNostr @nostr-fetch/adapter-rx-nostr rxNostrAdapter

Cancelling by AbortSignal

import { NostrFecher } from "nostr-fetch"

const fetcher = NostrFetcher.init();
const relayUrls = [/* relay URLs */];

const evIter = fetcher.allEventsIterator(
    relayUrls,
    {/* filter */},
    {/* time range */},
    /* pass an `AbortSignal` here to enable cancellation! */
    { signal: AbortSignal.timeout(1000) },
);

for await (const ev of evIter) {
    // ...
}

Examples

You can find example codes under packages/examples directory.

To run examples, follow the steps (using npm for example):

# first time only: install dependencies & build subpackages
npm install && npm run build


# then, execute example
# the command executes packages/examples/src/fetchAll.ts
npm run example fetchAll

# some examples takes a hex pubkey as an argument
npm run example fetchLastPerAuthor <your hex pubkey>

API

class NostrFetcher

The entry point of Nostr events fetching.

It manages connections to Nostr relays under the hood. It is recommended to reuse single NostrFetcher instance in entire app.

You should instantiate it with following initializers instead of the constructor.


Initializers and Finalizers

NostrFetcher.init

Initializes a NostrFetcher instance based on the default relay pool implementation.

NostrFetcher.withCustomPool

Initializes a NostrFetcher instance based on a custom relay pool implementation passed as an argument.

This opens up interoperability with other relay pool implementations such as nostr-tools' SimplePool. See here for details.

NostrFetcher#shutdown

Cleans up the internal relay pool.

If you use a fetcher instance initialized via NostrFetcher.init, calling this method closes connections to all the connected relays.

You can use a variable with using keyword to automatically shutdown a fetcher instance when the scope of the using variable ends. Roughly speaking, withFinally() and withUsing() in the code bellow are the same.

async function withFinally() {
    const fetcher = NostrFetcher.init();
    try {
        // do some work with the fetcher...
    } finally {
        fetcher.shutdown();
    }
}

async function withUsing() {
    using fetcher = NostrFetcher.init();
    // do some work with the fetcher...

    // the fetcher will be automatically shutdown here!
}

Fetch Methods

All methods are instance methods of NostrFetcher.

allEventsIterator

public allEventsIterator(
    relayUrls: string[],
    filter: FetchFilter,
    timeRangeFilter: FetchTimeRangeFilter,
    options?: AllEventsIterOptions
): AsyncIterable<NostrEvent>

Returns an async iterable of all events matching the filter from Nostr relays specified by the array of URLs.

You can iterate over events using for-await-of loop.

const fetcher = NostrFetcher.init();
const events = fetcher.allEventsIterator([/* relays */], {/* filter */}, {/* time range */});
for await (const ev of events) {
    // process events
}

Specifying enableBackpressure: true in options enables "backpressure mode", where the fetcher is backpressured by the consumer of the iterator.

Note

There are no guarantees about the order of returned events. Especially, events are not necessarily ordered in "newest to oldest" order.


fetchAllEvents

public async fetchAllEvents(
    relayUrls: string[],
    filter: FetchFilter,
    timeRangeFilter: FetchTimeRangeFilter,
    options?: FetchAllOptions
): Promise<NostrEvent[]>

Fetches all events matching the filter from Nostr relays specified by the array of URLs, and collect them into an array.

If sort: true is specified in options, events in the resulting array will be sorted in "newest to oldest" order.

Note

There are no guarantees about the order of returned events if sort options is not specified.


fetchLatestEvents

public async fetchLatestEvents(
    relayUrls: string[],
    filter: FetchFilter,
    limit: number,
    options?: FetchLatestOptions
): Promise<NostrEvent[]>

Fetches latest up to limit events matching the filter from Nostr relays specified by the array of URLs.

Events in the result will be sorted in "newest to oldest" order.


fetchLastEvent

public async fetchLastEvent(
    relayUrls: string[],
    filter: FetchFilter,
    options?: FetchLatestOptions
): Promise<NostrEvent | undefined>

Fetches the last event matching the filter from Nostr relays specified by the array of URLs.

Returns undefined if no event matching the filter exists in any relay.


fetchLatestEventsPerKey

public fetchLatestEventsPerKey<KN extends FetchFilterKeyName>(
    keyName: KN,
    keysAndRelays: KeysAndRelays<KN>,
    otherFilter: FetchFilter,
    limit: number,
    options?: FetchLatestOptions
): AsyncIterable<NostrEventListWithKey<KN>>

Fetches latest up to limit events for each key specified by keyName and keysAndRelays.

keysAndRelays can be either of two types:

  • { keys: K[], relayUrls: string[] }: The fetcher will use the same relay set (relayUrls) for all keys to fetch events.
  • Map<K, string[]>: Key must be the key of event and value must be relay set for that key. The fetcher will use separate relay set for each key to fetch events.

Note

The type K is number if keyName is "kinds". Otherwise, K is string.

Result is an async iterable of { key: <key of events>, events: <events which have that key> } pairs.

Each array of events in the result are sorted in "newest to oldest" order.


fetchLastEventPerKey

public fetchLatestEventsPerKey<KN extends FetchFilterKeyName>(
    keyName: KN,
    keysAndRelays: KeysAndRelays<KN>,
    otherFilter: FetchFilter,
    options?: FetchLatestOptions
): AsyncIterable<NostrEventWithKey<KN>>

Fetches the last event for each key specified by keysAndRelays.

keysAndRelays can be either of two types:

  • { keys: K[], relayUrls: string[] }: The fetcher will use the same relay set (relayUrls) for all keys to fetch events.
  • Map<K, string[]>: Key must be key of the event and value must be relay set for that key. The fetcher will use separate relay set for each key to fetch events.

Note

The type K is number if keyName is "kinds". Otherwise, K is string.

Result is an async iterable of { key: <key of events>, event: <the latest event which have that key> } pairs.

event in result will be undefined if no event matching the filter exists in any relay.


fetchLatestEventsPerAuthor

public fetchLatestEventsPerAuthor(
    authorsAndRelays: AuthorsAndRelays,
    otherFilter: Omit<FetchFilter, "authors">,
    limit: number,
    options: FetchLatestOptions = {}
): AsyncIterable<{ author: string; events: NostrEvent[] }>

Fetches latest up to limit events for each author specified by authorsAndRelays.

It is just a wrapper of fetchLatestEventsPerKey specialized to "authors" key.


fetchLastEventPerAuthor

public fetchLastEventPerAuthor(
    authorsAndRelays: AuthorsAndRelays,
    otherFilter: Omit<FetchFilter, "authors">,
    options: FetchLatestOptions = {}
): AsyncIterable<{ author: string; event: NostrEvent | undefined }>

Fetches the last event for each author specified by authorsAndRelays.

It is just a wrapper of fetchLastEventPerKey specialized to "authors" key.

Support me!

You can support this project by:

changelog

Change Log

All notable changes to this project will be documented in this file. See Conventional Commits for commit guidelines.

0.17.0 (2024-10-08)

Bug Fixes

  • compare event id with its hash in verifyEventSig (70bfafb)
  • export type EventVerifier (87db35d)

Features

  • make NostrFetcher disposeable (16ba9af)

0.16.0 (2024-10-04)

Bug Fixes

  • allow async-iter fetchers do clean up on early return/break (f79e6ef)
  • change default value for webSocketConstructor to globalThis.WebSocket so that correct error message is shown (d587d2b)
  • event ordering; event with lower id comes first if two events have the same created_at (70626e5)
  • make Filter type compatible with NDK (d9fb6f4)
  • remove dependency to NodeJS.Timeout type (986f6ae)
  • Revert "fix: respect NIP-01's note on reconnections to relays" (c3d2586)
  • timer leak on aborting fetch (707b9bd)
  • use Abortsignal.timeout instead (bc55b11)
  • use specified verifier for reduced verification (6dc2e22)
  • use specified verifier for reduced verification (37f53d1)

Features

  • add webSocketConstructor to option for NostrFetcher.init() (6335b4d)
  • add eventVerifier to FetcherOptions, to make the event verification logic customizable (75810b3)
  • add noopVerifier to exports (72cbc17)
  • allow async verifier (dfebbd5)
  • change option name: "abortSignal" -> "signal" (3d5e8f8)
  • deprecate eventKind constants (edbcc08)
  • deprecate skipVerification & reduceVerification options (6a43d71)
  • give up checking tag query key syntax statically (aa9d893)

0.15.1 (2024-03-20)

Note: Version bump only for package nostr-fetch-root

0.15.0 (2024-01-05)

Bug Fixes

  • use AbstractSimplePool as type of internal pool to accept arbitrary SimplePool impls (6ebe08f)

Features

  • add adapter for nostr-tools v2 (b6092df)

0.14.1 (2023-12-14)

Bug Fixes

  • adapter-ndk: bump minimum supported NDK version to 1.0.0 (1138901)

0.14.0 (2023-12-14)

Bug Fixes

  • add handling of subscription close by relay to default fetcher backend (193bfa5)
  • add R2C CLOSED message handling (6bdc45e)
  • make adapters aware of the skipFilterMatching option (461431c)
  • simplify event go-backing algorithm (9c2781e)
  • support CLOSED R2C message (316b99c)

Features

  • check if events match the filters by default (d6ce40d)

0.13.1 (2023-10-30)

Note: Version bump only for package nostr-fetch-root

0.13.0 (2023-09-01)

Bug Fixes

  • add standardized event kinds (NIP-52) (34875c2)
  • add standardized event kinds (NIP-72) (e867ba7)
  • type of timeout ID (87c5063)
  • type of timeout ID (1dbebe3)

Features

  • add abiility to fetch latest events as of specified time (98ed306)

0.12.2 (2023-07-24)

Bug Fixes

0.12.1 (2023-07-17)

Bug Fixes

  • unsubscribe observers on EOSE (f777959)

0.12.0 (2023-07-17)

Bug Fixes

  • change type of NOTICE message, fix broken tests (09ed97f)
  • ignore NOTICE if it doesn't have to do with REQs by fetcher (08c6545)
  • use @noble/curves instead of @noble/secp256k1 (2d64ac8)

Features

  • fetchLatestEventsPerKey (cbc544f)
  • rename normalizeRelayUrls -> normalizeRelayUrlSet (f2b5d2a)
  • rx-nostr adapter (7cdd6a3)

0.11.2 (2023-07-09)

Bug Fixes

0.11.1 (2023-07-09)

Bug Fixes

  • tsconfig for adapter-nostr-relaypool (be8edbc)

0.11.0 (2023-07-09)

Bug Fixes

  • bug in counting runnigSubs (1f4185d)
  • make adapters' fetchTillEose conform to new requirement of interface (2e9a17e)

Features

0.10.1 (2023-07-05)

Bug Fixes

  • ensure connection to relays every time fetchers open new subscription (75c230b)
  • respect NIP-01's note on reconnections to relays (d004560)

0.10.0 (2023-07-04)

Bug Fixes

  • add tests for default RelayPool (d5ab10b)
  • add unit tests for default Relay/Subscription (239964f)
  • bug in subscription auto abortion of SimplePool/NDK adapter (4fd8879)
  • bugs in querying supported NIPs (4950841)
  • close subscription on notice received in SimplePool adapter (2a26b4f)
  • handle error from sub.req/close (e84a060)
  • make tag query field optional (71a56a6)
  • mitigate test flakiness (cedbca2)
  • move "immediate abortion check" after the line that sends REQ etc (a05e6d4)
  • pass subId option correctly in SimplePool adapter (1a157b0)
  • remove unnecessary codes from RelayPool impl (b74da61)
  • separate interface of relay capability checking for testing (2b1ca1f)
  • throw error if time range is invalid (528268c)
  • write tests for adapter-ndk (6327ee5)
  • write tests for adapter-nostr-tools (1a851e9)
  • write tests for DefaultFetcherBase (e2f7f19)
  • write tests for fetcher helpers (bc67fbf)
  • write tests for nostr-relaypool adapter (b0aae57)
  • write tests for NostrFetcher based on fakes (ed06ef6)

Features

  • add buffered events count to stats (8093106)
  • add fetch progress tracking (9a51bc1)
  • add means to know on which relay an event was seen (f70042b)
  • add newly standardized event kinds (68252a4)
  • add search example (07b6a7f)
  • add support for search filter (d145de6)
  • basic fetch statistics (ccf60b9)
  • change semantics of fetchers which return an async iterable (1f08e59)
  • export type NostrFecthError from nostr-fetch (6da4515)
  • rename NostrFetcherBase to NostrFetcherBackend (0a36def)

0.9.0 (2023-06-16)

Features

  • add backpressure feature to Channel (1ef5596)
  • add option for enabling backpressure to allEventsIterator (ba4c126)
  • relax assertion of args to fetchers (16ad322)
  • separate relay set for each authors (41b2291)

0.8.0 (2023-06-11)

Bug Fixes

  • add periodic auto re-connection to the default relay pool (c974d0e)
  • change default debug log level (none -> warn) (6d14042)
  • fetchTillEose impls (ab4d6a5)
  • make format specs available in DebugLogger with prefix (ff69d0f)
  • reduct CloseEvent being passed to relay event lister (e214729)
  • replace all logForDebug with DebugLogger (807e110)
  • use subloggers for scoped log (297e8e4)
  • use subloggers for scoped log (adapters) (8c2bd9a)

Features

  • add adapter for NDK (6eaaf82)
  • add DebugLogger class with minimum log level support (086b139)
  • add NDK adapter example (f8891bb)
  • fetchers now allow only single filters per request (dbceb8d)
  • improve ergonomics of initializing fetchers (2a2070e)
  • rename closeAll -> shutdown (7c625e3)

0.7.0 (2023-06-02)

Bug Fixes

  • move responsibility of relay URL normalization to ensureRelays (c0e747d)
  • options for fetchLastEvent is now reflected properly (2009e2a)

Features

  • add new fetchers (32baa5b)
  • validate parameters for fetchers and throw if invalid (ca4e49d)

0.6.1 (2023-05-31)

Bug Fixes

  • adapter-nostr-relaypool: add kernel as dependency (ae22ff2)
  • adapter-nostr-relaypool: configure exports in package.json (43b02d7)

0.6.0 (2023-05-31)

Bug Fixes

  • bug in getRelayIfConnected (9fc0afb)
  • build config for adapter-nostr-relaypool (3e66a6d)
  • initiate sub auto abortion timer (0fce069)
  • make adapter work correctly (619dbd2)

Features

  • adapter-nostr-tools: rewrite adapter (4cc577a)
  • add adapter for nostr-relaypool (4ac6bca)
  • add example for nostr-relaypool interop (bf09918)
  • nostr-fetch: reimplement Fetcher based on new abstractions (97369e6)

0.5.4 (2023-05-31)

Bug Fixes

  • problem with import from ESM enabled node (0589d6b)
  • specify files for module/types conditions in exports (56e14c5)

0.5.3 (2023-05-18)

Bug Fixes

  • adapter-nostr-tools: bug in auto subscription abortion (d3453b4)

0.5.2 (2023-05-15)

Bug Fixes

  • adapter-nostr-tools: add means to log irregular events for debug to SimplePoolAdapter (6ba741d)
  • adapter-nostr-tools: minimize nostr-tools version as peer dep (68ed56f)
  • adapter-nostr-tools: subs from SimplePoolAdapter now support auto abortion (06e8110)
  • SimplePoolAdapter now respects connenctTimeoutMs (15034c8)