- Introduced comprehensive documentation for the Nostr Development Kit (NDK) including an overview, quick reference, and troubleshooting guide. - Added detailed examples covering initialization, authentication, event publishing, querying, and user profile management. - Structured the documentation to facilitate quick lookups and deep learning, based on real-world usage patterns from the Plebeian Market application. - Created an index for examples to enhance usability and navigation. - Bumped version to 1.0.0 to reflect the addition of this new skill set.
352 lines
6.9 KiB
Markdown
352 lines
6.9 KiB
Markdown
# NDK Quick Reference
|
|
|
|
Fast lookup guide for common NDK tasks.
|
|
|
|
## Quick Start
|
|
|
|
```typescript
|
|
import NDK from '@nostr-dev-kit/ndk'
|
|
|
|
const ndk = new NDK({ explicitRelayUrls: ['wss://relay.damus.io'] })
|
|
await ndk.connect()
|
|
```
|
|
|
|
## Authentication
|
|
|
|
### Browser Extension (NIP-07)
|
|
```typescript
|
|
import { NDKNip07Signer } from '@nostr-dev-kit/ndk'
|
|
const signer = new NDKNip07Signer()
|
|
await signer.blockUntilReady()
|
|
ndk.signer = signer
|
|
```
|
|
|
|
### Private Key
|
|
```typescript
|
|
import { NDKPrivateKeySigner } from '@nostr-dev-kit/ndk'
|
|
const signer = new NDKPrivateKeySigner(privateKeyHex)
|
|
await signer.blockUntilReady()
|
|
ndk.signer = signer
|
|
```
|
|
|
|
### Remote Signer (NIP-46)
|
|
```typescript
|
|
import { NDKNip46Signer, NDKPrivateKeySigner } from '@nostr-dev-kit/ndk'
|
|
const localSigner = new NDKPrivateKeySigner()
|
|
const remoteSigner = new NDKNip46Signer(ndk, bunkerUrl, localSigner)
|
|
await remoteSigner.blockUntilReady()
|
|
ndk.signer = remoteSigner
|
|
```
|
|
|
|
## Publish Event
|
|
|
|
```typescript
|
|
import { NDKEvent } from '@nostr-dev-kit/ndk'
|
|
|
|
const event = new NDKEvent(ndk)
|
|
event.kind = 1
|
|
event.content = "Hello Nostr!"
|
|
event.tags = [['t', 'nostr']]
|
|
|
|
await event.sign()
|
|
await event.publish()
|
|
```
|
|
|
|
## Query Events (One-time)
|
|
|
|
```typescript
|
|
const events = await ndk.fetchEvents({
|
|
kinds: [1],
|
|
authors: [pubkey],
|
|
limit: 50
|
|
})
|
|
|
|
// Convert Set to Array
|
|
const eventArray = Array.from(events)
|
|
```
|
|
|
|
## Subscribe (Real-time)
|
|
|
|
```typescript
|
|
const sub = ndk.subscribe(
|
|
{ kinds: [1], authors: [pubkey] },
|
|
{ closeOnEose: false }
|
|
)
|
|
|
|
sub.on('event', (event) => {
|
|
console.log('New event:', event.content)
|
|
})
|
|
|
|
// Cleanup
|
|
sub.stop()
|
|
```
|
|
|
|
## Get User Profile
|
|
|
|
```typescript
|
|
// By npub
|
|
const user = ndk.getUser({ npub })
|
|
const profile = await user.fetchProfile()
|
|
|
|
// By hex pubkey
|
|
const user = ndk.getUser({ hexpubkey: pubkey })
|
|
const profile = await user.fetchProfile()
|
|
|
|
// By NIP-05
|
|
const user = await ndk.getUserFromNip05('user@domain.com')
|
|
const profile = await user?.fetchProfile()
|
|
```
|
|
|
|
## Common Filters
|
|
|
|
```typescript
|
|
// By author
|
|
{ kinds: [1], authors: [pubkey] }
|
|
|
|
// By tag
|
|
{ kinds: [1], '#p': [pubkey] }
|
|
{ kinds: [30402], '#d': [productSlug] }
|
|
|
|
// By time
|
|
{
|
|
kinds: [1],
|
|
since: Math.floor(Date.now() / 1000) - 86400, // Last 24h
|
|
until: Math.floor(Date.now() / 1000)
|
|
}
|
|
|
|
// By event ID
|
|
{ ids: [eventId] }
|
|
|
|
// Multiple conditions
|
|
{
|
|
kinds: [16, 17],
|
|
'#order': [orderId],
|
|
since: timestamp,
|
|
limit: 100
|
|
}
|
|
```
|
|
|
|
## Tag Helpers
|
|
|
|
```typescript
|
|
// Get first tag value
|
|
const orderId = event.tagValue('order')
|
|
|
|
// Find specific tag
|
|
const tag = event.tags.find(t => t[0] === 'payment')
|
|
const value = tag?.[1]
|
|
|
|
// Get all of one type
|
|
const pTags = event.tags.filter(t => t[0] === 'p')
|
|
|
|
// Common tag formats
|
|
['p', pubkey] // Mention
|
|
['e', eventId] // Event reference
|
|
['t', 'nostr'] // Hashtag
|
|
['d', identifier] // Replaceable ID
|
|
['a', '30402:pubkey:d-tag'] // Addressable reference
|
|
```
|
|
|
|
## Error Handling Pattern
|
|
|
|
```typescript
|
|
const ndk = ndkActions.getNDK()
|
|
if (!ndk) throw new Error('NDK not initialized')
|
|
|
|
const signer = ndk.signer
|
|
if (!signer) throw new Error('No active signer')
|
|
|
|
try {
|
|
await event.publish()
|
|
} catch (error) {
|
|
console.error('Publish failed:', error)
|
|
throw error
|
|
}
|
|
```
|
|
|
|
## React Integration
|
|
|
|
```typescript
|
|
// Query function
|
|
export const fetchProducts = async (pubkey: string) => {
|
|
const ndk = ndkActions.getNDK()
|
|
if (!ndk) throw new Error('NDK not initialized')
|
|
|
|
const events = await ndk.fetchEvents({
|
|
kinds: [30402],
|
|
authors: [pubkey]
|
|
})
|
|
|
|
return Array.from(events)
|
|
}
|
|
|
|
// React Query hook
|
|
export const useProducts = (pubkey: string) => {
|
|
return useQuery({
|
|
queryKey: ['products', pubkey],
|
|
queryFn: () => fetchProducts(pubkey),
|
|
enabled: !!pubkey,
|
|
})
|
|
}
|
|
|
|
// Subscription in useEffect
|
|
useEffect(() => {
|
|
if (!ndk || !orderId) return
|
|
|
|
const sub = ndk.subscribe(
|
|
{ kinds: [16], '#order': [orderId] },
|
|
{ closeOnEose: false }
|
|
)
|
|
|
|
sub.on('event', () => {
|
|
queryClient.invalidateQueries(['order', orderId])
|
|
})
|
|
|
|
return () => sub.stop()
|
|
}, [ndk, orderId, queryClient])
|
|
```
|
|
|
|
## Common Event Kinds
|
|
|
|
```typescript
|
|
0 // Metadata (profile)
|
|
1 // Text note
|
|
4 // Encrypted DM (NIP-04)
|
|
7 // Reaction
|
|
9735 // Zap receipt
|
|
10000 // Mute list
|
|
10002 // Relay list
|
|
30402 // Product listing (Marketplace)
|
|
31990 // App handler (NIP-89)
|
|
```
|
|
|
|
## Relay Management
|
|
|
|
```typescript
|
|
// Check connection
|
|
const connected = ndk.pool?.connectedRelays().length > 0
|
|
|
|
// Get connected relays
|
|
const relays = Array.from(ndk.pool?.relays.values() || [])
|
|
.filter(r => r.status === 1)
|
|
|
|
// Add relay
|
|
ndk.addExplicitRelay('wss://relay.example.com')
|
|
```
|
|
|
|
## Connection with Timeout
|
|
|
|
```typescript
|
|
const connectWithTimeout = async (timeoutMs = 10000) => {
|
|
const connectPromise = ndk.connect()
|
|
const timeoutPromise = new Promise((_, reject) =>
|
|
setTimeout(() => reject(new Error('Timeout')), timeoutMs)
|
|
)
|
|
|
|
await Promise.race([connectPromise, timeoutPromise])
|
|
}
|
|
```
|
|
|
|
## Current User
|
|
|
|
```typescript
|
|
// Active user
|
|
const user = ndk.activeUser
|
|
|
|
// From signer
|
|
const user = await ndk.signer?.user()
|
|
|
|
// User info
|
|
const pubkey = user.pubkey // hex
|
|
const npub = user.npub // NIP-19
|
|
```
|
|
|
|
## Parameterized Replaceable Events
|
|
|
|
```typescript
|
|
// Create
|
|
const event = new NDKEvent(ndk)
|
|
event.kind = 30402
|
|
event.content = JSON.stringify(data)
|
|
event.tags = [
|
|
['d', uniqueIdentifier], // Required for replaceable
|
|
['title', 'Product Name'],
|
|
]
|
|
|
|
await event.sign()
|
|
await event.publish()
|
|
|
|
// Query (returns latest only)
|
|
const events = await ndk.fetchEvents({
|
|
kinds: [30402],
|
|
authors: [pubkey],
|
|
'#d': [identifier]
|
|
})
|
|
```
|
|
|
|
## Validation Checks
|
|
|
|
```typescript
|
|
// Event age check
|
|
const now = Math.floor(Date.now() / 1000)
|
|
const age = now - (event.created_at || 0)
|
|
if (age > 86400) console.log('Event older than 24h')
|
|
|
|
// Required fields
|
|
if (!event.pubkey || !event.created_at || !event.sig) {
|
|
throw new Error('Invalid event')
|
|
}
|
|
|
|
// Tag existence
|
|
const orderId = event.tagValue('order')
|
|
if (!orderId) throw new Error('Missing order tag')
|
|
```
|
|
|
|
## Performance Tips
|
|
|
|
```typescript
|
|
// Batch queries
|
|
const [products, orders] = await Promise.all([
|
|
ndk.fetchEvents(productFilter),
|
|
ndk.fetchEvents(orderFilter)
|
|
])
|
|
|
|
// Limit results
|
|
const filter = {
|
|
kinds: [1],
|
|
limit: 50,
|
|
since: recentTimestamp
|
|
}
|
|
|
|
// Cache with React Query
|
|
const { data } = useQuery({
|
|
queryKey: ['profile', npub],
|
|
queryFn: () => fetchProfile(npub),
|
|
staleTime: 5 * 60 * 1000, // 5 min
|
|
})
|
|
```
|
|
|
|
## Debugging
|
|
|
|
```typescript
|
|
// Check NDK state
|
|
console.log('Connected:', ndk.pool?.connectedRelays())
|
|
console.log('Signer:', ndk.signer)
|
|
console.log('Active user:', ndk.activeUser)
|
|
|
|
// Event inspection
|
|
console.log('Event ID:', event.id)
|
|
console.log('Tags:', event.tags)
|
|
console.log('Content:', event.content)
|
|
console.log('Author:', event.pubkey)
|
|
|
|
// Subscription events
|
|
sub.on('event', e => console.log('Event:', e))
|
|
sub.on('eose', () => console.log('End of stored events'))
|
|
```
|
|
|
|
---
|
|
|
|
For detailed explanations and advanced patterns, see `ndk-skill.md`.
|
|
|