Primal vs Nostr

Overview

Primal's cache server is a powerful extension layer built on top of the Nostr protocol. While standard NIP-01 relays provide basic event storage and filtering, Primal adds pre-computed analytics, trending algorithms, search capabilities, and rich social features that would be difficult or impossible to achieve efficiently with plain Nostr relays alone.

This document compares Primal's cache endpoints against what can be achieved using standard Nostr NIP-01 filters with basic relays. The goal is to understand:

- What Primal uniquely provides - Features requiring server-side computation, aggregation, or curation
- What standard Nostr can do - Core functionality achievable with NIP-01 filters across any compliant relay
- Where they overlap - Functionality that both can provide, with Primal offering convenience or performance benefits

The Baseline: Standard Nostr (NIP-01)

Standard Nostr relays implement NIP-01, providing:

- Event storage and retrieval via WebSocket connections
- Filtering by event IDs, authors, kinds, tags, and timestamps
- Subscriptions for real-time event updates
- Simple publishing of signed events

What NIP-01 relays cannot do efficiently:

- Full-text search across event content
- Trending/scoring algorithms
- Analytics and aggregated statistics
- Content recommendations
- Media processing and CDN
- User relationship graphs (who follows whom)
- Reverse lookups (e.g., "who mentioned this event?")

Primal's Value Proposition

Primal's cache server provides:

1. Pre-computed Analytics - Follower counts, engagement metrics, trending scores
2. Search - Full-text search across notes and user profiles
3. Discovery - Trending content, recommended users, popular hashtags
4. Performance - Cached aggregations instead of expensive client-side computation
5. Rich Metadata - Media thumbnails, link previews, user scores
6. Convenience - Single-request APIs for multi-step operations
7. Premium Features - Membership management, content backup, media uploads

Filter Templates

Many Nostr queries follow predictable filter templates - parameterized patterns similar to SQL prepared statements. For example, to get reactions to an event:

{"kinds": [7], "#e": ["<event-id>"]}

Where all elements are fixed except <event-id>. This template-based thinking helps identify:

- Which queries can be standardized across clients
- Where Primal provides unique value beyond standard filters
- Opportunities for relay optimization

Primal Server Methods

Quick Reference

Legend:
- ✅ = Personalized functionality requiring realtime data
- ❌ = Has standard Nostr filter equivalent OR widely-accepted community standard
- 🟡 = Could be replicated by a bot publishing data as Nostr events on a schedule

Network & Statistics
- 🟡 net_stats
- 🟡 nostr_stats
- 🟡 server_name

Feed Endpoints
- ❌ feed (notes: "authored")
- ❌ feed (notes: "follows")
- ❌ feed (notes: "replies")
- ❌ feed (notes: "bookmarks")
- ✅ feed (notes: "user_media_thumbnails")
- ❌ feed_2
- ❌ thread_view

User Profile & Social Graph
- ❌ user_profile
- ❌ user_infos
- ❌ contact_list
- ❌ is_user_following
- ❌ user_followers
- ❌ mutual_follows
- ✅ user_profile_followed_by

Events & Actions
- ❌ events
- ❌ event_actions (all kinds)

Zaps
- ✅ zaps_feed
- ❌ user_zaps
- ✅ user_zaps_by_satszapped
- ✅ user_zaps_sent
- ✅ event_zaps_by_satszapped
- ✅ invoices_to_zap_receipts

Direct Messages
- ❌ get_directmsg_contacts
- ❌ get_directmsgs
- ✅ directmsg_count
- ✅ directmsg_count_2
- ✅ reset_directmsg_count
- ✅ reset_directmsg_counts

Content Moderation
- ❌ mutelist
- ❌ mutelists
- ✅ allowlist
- ❌ is_hidden_by_content_moderation

Explore & Discovery
- 🟡 explore_zaps
- 🟡 explore_people
- 🟡 explore_media
- 🟡 explore_topics
- 🟡 scored
- 🟡 scored_users
- 🟡 scored_users_24h
- 🟡 explore_global_trending_24h
- 🟡 explore_global_mostzapped_4h
- 🟡 explore
- ✅ explore_legend_counts

Search
- ❌ search
- ❌ advanced_search
- ❌ user_search

Long-form Content
- ❌ long_form_content_feed
- ❌ long_form_content_thread_view
- 🟡 get_recommended_reads
- 🟡 get_reads_topics
- 🟡 get_featured_authors
- ✅ articles_stats
- ❌ drafts
- ✅ top_article

Bookmarks & Highlights
- ❌ get_bookmarks
- ❌ get_highlights

Relays
- ❌ get_user_relays
- ❌ get_user_relays_2
- 🟡 relays
- 🟡 get_default_relays

Recommendations
- 🟡 get_recommended_users
- 🟡 get_suggested_users

Feeds & Directives
- ✅ feed_directive
- ✅ feed_directive_2
- ✅ mega_feed_directive
- ✅ advanced_feed
- 🟡 get_advanced_feeds
- 🟡 get_home_feeds
- 🟡 get_reads_feeds
- ✅ enrich_feed_events

DVM
- 🟡 get_dvm_feeds
- ✅ dvm_feed_info
- 🟡 get_featured_dvm_feeds

Settings & Configuration
- ❌ set_app_settings
- ❌ get_app_settings
- ❌ get_app_settings_2
- 🟡 get_default_app_settings
- ❌ set_app_subsettings
- ❌ get_app_subsettings
- 🟡 get_default_app_subsettings
- 🟡 client_config
- 🟡 get_app_releases

Notifications
- ❌ notifications
- ✅ notification_counts
- ✅ notification_counts_2
- ❌ get_notifications
- ✅ set_notifications_seen
- ✅ get_notifications_seen

Push Notifications
- ✅ update_push_notification_token
- ✅ update_push_notification_token_for_nip46
- ❌ events_nip46

Hashtags & Trending
- 🟡 trending_hashtags_4h
- 🟡 trending_hashtags_7d
- 🟡 trending_images
- 🟡 trending_images_4h

Lists & Collections
- ❌ parameterized_replaceable_list
- ❌ parametrized_replaceable_event
- ❌ parametrized_replaceable_events
- ❌ replaceable_event
- ❌ follow_lists
- ❌ follow_list

Content Filtering
- ✅ search_filterlist
- ✅ get_filterlist
- ✅ check_filterlist

Reporting
- ❌ report_user
- ❌ report_note

Event Broadcasting
- ❌ import_events
- ❌ broadcast_reply
- ❌ broadcast_events

Membership Features
- ✅ membership_media_management_stats
- ✅ membership_media_management_uploads
- ✅ membership_media_management_delete
- ❌ membership_recovery_contact_lists
- ❌ membership_recover_contact_list
- ❌ membership_content_stats
- ❌ membership_content_backup
- ✅ membership_content_rebroadcast_start
- ✅ membership_content_rebroadcast_cancel
- ✅ membership_content_rebroadcast_status
- ✅ rebroadcasting_status
- 🟡 membership_legends_leaderboard
- 🟡 membership_premium_leaderboard
- ❌ creator_paid_tiers

Media Management
- ❌ upload
- ❌ upload_chunk
- ❌ upload_complete
- ❌ upload_cancel
- ❌ get_media_metadata
- ❌ get_recommended_blossom_servers

Utilities
- ✅ user_of_ln_address
- ❌ nip19_decode
- ✅ set_last_time_user_was_online
- ✅ trusted_users
- ❌ note_mentions
- ✅ note_mentions_count
- ❌ find_reposts
- ✅ user_profile_scored_content
- ✅ user_profile_scored_media_thumbnails

---

Detailed Mappings

The following table maps Primal cache endpoints to their equivalent standard Nostr filters (where applicable). Some endpoints provide pre-computed analytics or complex aggregations that cannot be replicated with a single standard filter.

Network & Statistics

net_stats - Network statistics
No equivalent standard filter - aggregates global statistics

nostr_stats - Nostr network stats
No equivalent standard filter - aggregates network-wide metrics

server_name - Get server name
No equivalent standard filter - server metadata

Feed Endpoints

feed (notes: "authored") - User's posts

{"kinds": [1], "authors": ["<pubkey>"], "limit": <n>}

feed (notes: "follows") - Following feed

// Step 1: Get contact list
{"kinds": [3], "authors": ["<pubkey>"]}

// Step 2: Get posts from followed users (extract pubkeys from step 1 tags)
{"kinds": [1], "authors": ["<followed_pubkey1>", "<followed_pubkey2>", ...], "limit": <n>}

feed (notes: "replies") - User's replies

{"kinds": [1], "authors": ["<pubkey>"], "#e": [""], "limit": <n>}

Note: Standard filters don't support wildcard tag matching

feed (notes: "bookmarks") - Bookmarked posts

// Step 1: Get bookmark list
{"kinds": [10003], "authors": ["<pubkey>"]}
// Or for categorized bookmarks: {"kinds": [30003], "authors": ["<pubkey>"]}

// Step 2: Get bookmarked events (extract event IDs from step 1 tags)
{"ids": ["<bookmarked_id1>", "<bookmarked_id2>", ...]}

feed (notes: "user_media_thumbnails") - Posts with media
No single filter - requires content parsing to identify media

feed_2 - Extended feed endpoint
Similar to feed, see above variants

thread_view - Conversation thread

// Step 1: Get the target event
{"ids": ["<event_id>"]}

// Step 2: Get replies to the event
{"kinds": [1], "#e": ["<event_id>"], "limit": <n>}

// Step 3: Get parent events (parse "e" tags from step 1, recursively fetch ancestors)
{"ids": ["<parent_id1>", "<parent_id2>", ...]}

User Profile & Social Graph

user_profile - User profile information

{"kinds": [0], "authors": ["<pubkey>"]}

Note: Does not include follower counts or analytics provided by Primal

user_infos - Batch user information

{"kinds": [0], "authors": ["<pubkey1>", "<pubkey2>", ...]}

Note: Does not include scoring, follower counts, or other analytics

contact_list - User's contact list

{"kinds": [3], "authors": ["<pubkey>"]}

is_user_following - Check if user follows another

// Step 1: Get follower's contact list
{"kinds": [3], "authors": ["<follower_pubkey>"]}

// Step 2: Parse tags to check if target_pubkey is in the "p" tags

user_followers - Get user followers

{"kinds": [3], "#p": ["<pubkey>"]}

mutual_follows - Mutual followers between users

// Step 1: Get user A's contact list
{"kinds": [3], "authors": ["<pubkey_a>"]}

// Step 2: Get user B's contact list
{"kinds": [3], "authors": ["<pubkey_b>"]}

// Step 3: Compare "p" tags to find mutual follows

user_profile_followed_by - Who follows this user
No standard filter - requires reverse contact list lookup

Events & Actions

events - Get specific events

{"ids": ["<event_id1>", "<event_id2>", ...]}

event_actions (kind: 7) - Event reactions

{"kinds": [7], "#e": ["<event_id>"], "limit": <n>}

event_actions (kind: 1) - Event replies

{"kinds": [1], "#e": ["<event_id>"], "limit": <n>}

event_actions (kind: 6) - Event reposts

{"kinds": [6], "#e": ["<event_id>"], "limit": <n>}

event_actions (kind: 9735) - Event zaps

{"kinds": [9735], "#e": ["<event_id>"], "limit": <n>}

Zaps

zaps_feed - Zap feed
No single filter - complex query across zap receipts with context enrichment

user_zaps - Zaps received by user

{"kinds": [9735], "#p": ["<pubkey>"], "limit": <n>}

user_zaps_by_satszapped - Zaps sorted by amount
No equivalent - standard filters don't support sorting by zap amount

user_zaps_sent - Zaps sent by user
No standard filter - requires parsing bolt11 invoices in kind 9735 events

event_zaps_by_satszapped - Zaps on event sorted by amount
No equivalent - requires parsing and sorting by invoice amount

invoices_to_zap_receipts - Convert invoices to zap receipts
No equivalent - requires payment hash matching

Direct Messages

get_directmsg_contacts - Get DM contacts

// Step 1: Get DMs where user is receiver
{"kinds": [4], "#p": ["<user_pubkey>"], "limit": <n>}

// Step 2: Get DMs where user is sender
{"kinds": [4], "authors": ["<user_pubkey>"], "limit": <n>}

// Step 3: Extract unique pubkeys from authors and "p" tags

get_directmsgs - Get direct messages

// Step 1: Get messages from sender to receiver
{"kinds": [4], "authors": ["<sender>"], "#p": ["<receiver>"], "limit": <n>}

// Step 2: Get messages from receiver to sender
{"kinds": [4], "authors": ["<receiver>"], "#p": ["<sender>"], "limit": <n>}

// Step 3: Merge and sort by created_at for conversation view

directmsg_count - DM count
No equivalent - requires counting and aggregating

directmsg_count_2 - Extended DM count
No equivalent - requires counting and aggregating

reset_directmsg_count - Reset DM counter
No equivalent - stateful operation

reset_directmsg_counts - Reset all DM counters
No equivalent - stateful operation

Content Moderation

mutelist - Get mute list

{"kinds": [10000], "authors": ["<pubkey>"]}

mutelists - Get multiple mute lists

{"kinds": [10000], "authors": ["<pubkey1>", "<pubkey2>", ...]}

allowlist - Get allow list
No standard kind - implementation-specific

is_hidden_by_content_moderation - Check if content is hidden

// Step 1: Get user's mute list
{"kinds": [10000], "authors": ["<viewer_pubkey>"]}

// Step 2: Check if event author or event ID is in mute list tags

Explore & Discovery

explore_zaps - Trending zaps
No equivalent - requires scoring algorithm across zap receipts

explore_people - Discover users
No equivalent - requires recommendation algorithm

explore_media - Trending media
No equivalent - requires content analysis and trending algorithm

explore_topics - Trending topics
No equivalent - requires hashtag extraction and scoring

scored - Get scored/trending content
No equivalent - requires proprietary scoring algorithms

scored_users - Trending users
No equivalent - requires user scoring algorithm

scored_users_24h - Trending users (24h)
No equivalent - requires user scoring algorithm

explore_global_trending_24h - Global trending (24h)
No equivalent - requires trending algorithm

explore_global_mostzapped_4h - Most zapped globally (4h)
No equivalent - requires zap aggregation and sorting

explore - General explore endpoint
No equivalent - complex multi-mode discovery system

explore_legend_counts - Network statistics for user
No equivalent - aggregated statistics

Search

search - Search events

{"kinds": [1], "search": "<query>", "limit": <n>}

Using NIP-50 search filter

advanced_search - Advanced search with filters

{"kinds": [<kind_filter>], "search": "<query>", "limit": <n>}

Using NIP-50 search filter with kind filtering

user_search - Search users

{"kinds": [0], "search": "<query>", "limit": <n>}

Using NIP-50 search filter on user metadata events

Long-form Content (Articles)

long_form_content_feed - Article feed

{"kinds": [30023], "authors": ["<pubkey>"], "limit": <n>}

long_form_content_thread_view - Article with comments

// Step 1: Get the article
{"ids": ["<article_event_id>"]}

// Step 2: Get comments on the article (using "a" tag with kind:pubkey:d-tag format)
{"kinds": [1], "#a": ["30023:<pubkey>:<d_tag>"], "limit": <n>}

get_recommended_reads - Recommended articles
No equivalent - requires recommendation algorithm

get_reads_topics - Article topics
No equivalent - requires topic extraction and aggregation

get_featured_authors - Featured article authors
No equivalent - requires curation or scoring

articles_stats - Article statistics
No equivalent - aggregated statistics

drafts - Article drafts

{"kinds": [31234], "#k": ["30023"], "authors": ["<pubkey>"]}

Draft events using kind 31234

top_article - Top article
No equivalent - requires scoring algorithm

Bookmarks & Highlights

get_bookmarks - Get bookmarks

{"kinds": [10003], "authors": ["<pubkey>"]}

Or kind 30003 for categorized bookmarks

get_highlights - Get highlights

{"kinds": [9802], "authors": ["<pubkey>"]}

Relays

get_user_relays - User relay list

{"kinds": [10002], "authors": ["<pubkey>"]}

Or kind 10063 for NIP-65 relay list metadata

get_user_relays_2 - Extended relay list
Same as above

relays - Popular relays
No equivalent - requires relay usage statistics

get_default_relays - Default relay recommendations
No equivalent - server configuration

Recommendations

get_recommended_users - Recommended users to follow
No equivalent - requires recommendation algorithm

get_suggested_users - Suggested user accounts
No equivalent - requires recommendation algorithm

Feeds & Directives

feed_directive - Custom feed directive
No equivalent - complex feed specification system

feed_directive_2 - Extended feed directive
No equivalent - complex feed specification system

mega_feed_directive - Mega feed endpoint
No equivalent - complex feed specification system

advanced_feed - Advanced feed
No equivalent - complex feed specification system

get_advanced_feeds - Available advanced feeds
No equivalent - server configuration

get_home_feeds - Home feed configurations
No equivalent - server configuration

get_reads_feeds - Article feed configurations
No equivalent - server configuration

enrich_feed_events - Enrich feed with metadata
No equivalent - data enrichment operation

DVM (Data Vending Machines)

get_dvm_feeds - DVM feed list
No equivalent - DVM-specific aggregation

dvm_feed_info - DVM feed details
No equivalent - DVM-specific metadata

get_featured_dvm_feeds - Featured DVM feeds
No equivalent - DVM curation

Settings & Configuration

set_app_settings - Set user app settings
Community standard: Publish kind 30078 event with "d" tag "primal_app_settings" to user's relays. See NIP-78 for application-specific data storage.

get_app_settings - Get app settings

{"kinds": [30078], "authors": ["<pubkey>"], "#d": ["primal_app_settings"]}

Using NIP-78 application-specific data

get_app_settings_2 - Extended app settings
Same as above

get_default_app_settings - Default app settings
No equivalent - server configuration

set_app_subsettings - Set app subsettings
Community standard: Publish kind 30078 event with appropriate "d" tag to user's relays. See NIP-78 for application-specific data storage.

get_app_subsettings - Get app subsettings
Similar to app_settings with different "d" tag

get_default_app_subsettings - Default subsettings
No equivalent - server configuration

client_config - Client configuration
No equivalent - server configuration

get_app_releases - App release information
No equivalent - server metadata

Notifications

notifications - Get notifications

// Step 1: Get mentions
{"kinds": [1], "#p": ["<pubkey>"], "limit": <n>}

// Step 2: Get reactions to user's posts
{"kinds": [7], "#p": ["<pubkey>"], "limit": <n>}

// Step 3: Get zaps
{"kinds": [9735], "#p": ["<pubkey>"], "limit": <n>}

// Step 4: Get reposts (requires first getting user's posts, then finding kind 6 events)
// This is complex - need user's post IDs first

// Step 5: Merge, deduplicate, and sort by created_at

notification_counts - Notification counts
No equivalent - requires aggregation and counting

notification_counts_2 - Extended notification counts
No equivalent - requires aggregation and counting

get_notifications - Get notifications with filters
Same multi-step process as notifications but with additional filtering by notification type

set_notifications_seen - Mark notifications as seen
No equivalent - stateful write operation

get_notifications_seen - Get seen notification timestamp
No equivalent - requires fetching user state

Push Notifications

update_push_notification_token - Update push token
No equivalent - stateful write operation

update_push_notification_token_for_nip46 - Update push token for NIP-46
No equivalent - stateful write operation

events_nip46 - NIP-46 events

{"kinds": [24133], "#p": ["<pubkey>"]}

NIP-46 nostr connect events

Hashtags & Trending

trending_hashtags_4h - Trending hashtags (4h)
No equivalent - requires hashtag extraction and trending algorithm

trending_hashtags_7d - Trending hashtags (7d)
No equivalent - requires hashtag extraction and trending algorithm

trending_images - Trending images
No equivalent - requires media extraction and trending algorithm

trending_images_4h - Trending images (4h)
No equivalent - requires media extraction and trending algorithm

Lists & Collections

parameterized_replaceable_list - Get parameterized replaceable list

{"kinds": [<kind>], "authors": ["<pubkey>"], "#d": ["<identifier>"]}

parametrized_replaceable_event - Get single parameterized event

{"kinds": [<kind>], "authors": ["<pubkey>"], "#d": ["<identifier>"]}

parametrized_replaceable_events - Get multiple parameterized events

{"kinds": [<kind>], "authors": ["<pubkey>"], "#d": ["<id1>", "<id2>", ...]}

replaceable_event - Get replaceable event

{"kinds": [<kind>], "authors": ["<pubkey>"]}

follow_lists - Get follow lists

{"kinds": [30000], "authors": ["<pubkey>"]}

follow_list - Get specific follow list

{"kinds": [30000], "authors": ["<pubkey>"], "#d": ["<identifier>"]}

Content Filtering

search_filterlist - Search filter list
No equivalent - requires search across filter lists

get_filterlist - Get filter list
No standard kind - implementation-specific

check_filterlist - Check if filtered
No equivalent - requires evaluating filter lists

Reporting

report_user - Report user
Standard approach: Publish kind 1984 event with "p" tag for reported user and uppercase "P" tag for report recipient (Primal's pubkey). See NIP-56 for reporting specification.

report_note - Report note
Standard approach: Publish kind 1984 event with "e" tag for reported event and uppercase "P" tag for report recipient (Primal's pubkey). See NIP-56 for reporting specification.

Event Broadcasting

import_events - Import events
Community standard: Clients should directly broadcast events to relays using NIP-01 "EVENT" messages

broadcast_reply - Broadcast reply
Community standard: Clients should directly broadcast events to relays using NIP-01 "EVENT" messages

broadcast_events - Broadcast multiple events
Community standard: Clients should directly broadcast events to relays using NIP-01 "EVENT" messages

Membership Features

membership_media_management_stats - Media storage stats
No equivalent - Primal-specific membership feature

membership_media_management_uploads - List uploads
No equivalent - Primal-specific membership feature

membership_media_management_delete - Delete uploads
No equivalent - Primal-specific write operation

membership_recovery_contact_lists - Recover contact lists

// Query kind 3 events, one per day (keeping the one with most follows per day)
{"kinds": [3], "authors": ["<pubkey>"], "limit": 30}

Returns the most comprehensive contact list event from each day over the last 30 days

membership_recover_contact_list - Recover specific contact list

{"ids": ["<event_id>"]}

Simply retrieves a specific kind 3 event by ID

membership_content_stats - Content backup stats
No standard filter - requires counting events by kind across user's history. Client can fetch all user events and compute stats locally.

membership_content_backup - Backup content

{"kinds": [<kind_filter>], "authors": ["<pubkey>"], "since": <timestamp>, "limit": <n>}

Retrieves user's events for backup purposes - standard event query

membership_content_rebroadcast_start - Start rebroadcast
No equivalent - Primal-specific write operation

membership_content_rebroadcast_cancel - Cancel rebroadcast
No equivalent - Primal-specific write operation

membership_content_rebroadcast_status - Rebroadcast status
No equivalent - Primal-specific status query

rebroadcasting_status - Get rebroadcast status
No equivalent - Primal-specific status query

membership_legends_leaderboard - Legends leaderboard
No equivalent - Primal-specific membership feature

membership_premium_leaderboard - Premium leaderboard
No equivalent - Primal-specific membership feature

creator_paid_tiers - Creator subscription tiers
No equivalent - Primal-specific creator economy feature

Media Management

upload - Upload media
Community standard: Use Blossom for decentralized media storage

upload_chunk - Upload media chunk
Community standard: Use Blossom for decentralized media storage

upload_complete - Complete chunked upload
Community standard: Use Blossom for decentralized media storage

upload_cancel - Cancel upload
Community standard: Use Blossom for decentralized media storage

get_media_metadata - Get media metadata
Community standard: Use Blossom for decentralized media storage and metadata

get_recommended_blossom_servers - Blossom server recommendations
Community standard: Use Blossom server discovery mechanisms

Utilities

user_of_ln_address - Get pubkey from Lightning address
No equivalent - requires NIP-05/Lightning address resolution

nip19_decode - Decode NIP-19 identifiers
⚠️ Community standard: Use client-side libraries (NIP-19). This is a deterministic algorithm available in every programming language. There is no need for a server endpoint for this.

set_last_time_user_was_online - Update online status
No equivalent - stateful write operation

trusted_users - Get trusted users
No equivalent - requires trust graph computation

note_mentions - Get note mentions

{"kinds": [1], "#e": ["<event_id>"]}

note_mentions_count - Count note mentions
No equivalent - requires counting aggregation

find_reposts - Find reposts of event

{"kinds": [6], "#e": ["<event_id>"]}

user_profile_scored_content - User's top content
No equivalent - requires scoring algorithm

user_profile_scored_media_thumbnails - User's top media
No equivalent - requires content analysis and scoring*

Template Variations

Some templates have natural variations that maintain the same semantic purpose:

Reactions (Detailed)

{
  "kinds": [7],
  "#e": ["<event-id>"],
  "authors": ["<pubkey>"]
}

This is a refinement of the basic reaction template, filtering for a specific user's reaction to an event.

Multi-Kind Queries

{
  "kinds": [1, 6],
  "authors": ["<pubkey>"]
}

Fetching both regular posts (kind 1) and reposts (kind 6) from an author.

Anti-Patterns

Some filter constructions, while valid, don't follow clean template patterns:

Over-Specified Filters

{
  "ids": ["<event-id>"],
  "authors": ["<pubkey>"],
  "kinds": [1]
}

When querying by ids, other filters are typically redundant (event IDs are globally unique).

Unbounded Queries

{
  "kinds": [1]
}

Without limit, authors, or time bounds, this requests the entire history of kind 1 events - likely to be rejected by relays.

Implementation Guidance

When building Nostr applications:

1. Define filter factories for each template your app uses
2. Name your templates semantically (e.g., getUserProfile, getEventReactions)
3. Document parameters clearly for each template
4. Validate inputs before substitution to prevent malformed filters
5. Consider limits - always include sensible limit values for potentially large result sets

Example Factory Pattern

const FilterTemplates = {
  userProfile: (pubkey: string) => ({
    kinds: [0],
    authors: [pubkey]
  }),
  
  eventReactions: (eventId: string, limit?: number) => ({
    kinds: [7],
    "#e": [eventId],
    ...(limit && { limit })
  }),
  
  followingFeed: (pubkeys: string[], limit: number = 20) => ({
    kinds: [1],
    authors: pubkeys,
    limit
  })
};

Conclusion

By thinking of Nostr filters as templates rather than arbitrary JSON structures, we gain clarity in our application design, improve performance through predictable patterns, and build more maintainable codebases. This mental model bridges the gap between Nostr's flexible filter system and the structured query approaches developers are familiar with from traditional databases.