Navigation: Docs index · Getting started · API how-to · API reference
This guide demonstrates common use cases for the nostr-java library using GenericEvent, GenericTag, and NostrRelayClient.
- Setup
- Text Notes (NIP-01)
- Metadata Events (NIP-01)
- Encrypted Direct Messages (NIP-04/44)
- Event Deletion (NIP-09)
- Reactions (NIP-25)
- Replaceable Events
- Ephemeral Events
- Filters and Subscriptions
- Async Operations
All examples use two identities (sender and recipient) and a relay:
import nostr.base.Kinds;
import nostr.client.springwebsocket.NostrRelayClient;
import nostr.event.impl.GenericEvent;
import nostr.event.message.EventMessage;
import nostr.event.tag.GenericTag;
import nostr.id.Identity;
private static final Identity SENDER = Identity.generateRandomIdentity();
private static final Identity RECIPIENT = Identity.generateRandomIdentity();
private static final String RELAY = "wss://relay.398ja.xyz";Purpose: Post public text messages.
GenericEvent note = GenericEvent.builder()
.pubKey(SENDER.getPublicKey())
.kind(Kinds.TEXT_NOTE)
.content("Hello world, I'm here on nostr-java!")
.tags(List.of(
GenericTag.of("p", RECIPIENT.getPublicKey().toString())
))
.build();
SENDER.sign(note);
try (NostrRelayClient client = new NostrRelayClient(RELAY)) {
client.send(new EventMessage(note));
}With hashtags:
GenericEvent tagged = GenericEvent.builder()
.pubKey(SENDER.getPublicKey())
.kind(Kinds.TEXT_NOTE)
.content("Check out #nostr! cc @someone")
.tags(List.of(
GenericTag.of("t", "nostr"),
GenericTag.of("p", somePublicKeyHex)
))
.build();Purpose: Publish user profile information.
String profileJson = """
{
"name": "Nostr Guy",
"about": "It's me!",
"picture": "https://example.com/avatar.jpg",
"nip05": "guy@nostr-java.io"
}
""";
GenericEvent metadata = GenericEvent.builder()
.pubKey(SENDER.getPublicKey())
.kind(Kinds.SET_METADATA)
.content(profileJson)
.build();
SENDER.sign(metadata);NIP-04 (legacy):
import nostr.encryption.MessageCipher04;
MessageCipher04 cipher = new MessageCipher04(
SENDER.getPrivateKey(),
RECIPIENT.getPublicKey()
);
String encrypted = cipher.encrypt("Hello Nakamoto!");
GenericEvent dm = GenericEvent.builder()
.pubKey(SENDER.getPublicKey())
.kind(Kinds.ENCRYPTED_DIRECT_MESSAGE)
.content(encrypted)
.tags(List.of(
GenericTag.of("p", RECIPIENT.getPublicKey().toString())
))
.build();
SENDER.sign(dm);NIP-44 (recommended):
import nostr.encryption.MessageCipher44;
MessageCipher44 cipher = new MessageCipher44(
SENDER.getPrivateKey(),
RECIPIENT.getPublicKey()
);
String encrypted = cipher.encrypt("Secret message");
String decrypted = cipher.decrypt(encrypted);Purpose: Request deletion of previously published events.
GenericEvent deletion = GenericEvent.builder()
.pubKey(SENDER.getPublicKey())
.kind(Kinds.DELETION)
.content("Deleting old posts")
.tags(List.of(
GenericTag.of("e", eventIdToDelete1),
GenericTag.of("e", eventIdToDelete2)
))
.build();
SENDER.sign(deletion);Purpose: React to events with likes or emoji.
// Like reaction
GenericEvent like = GenericEvent.builder()
.pubKey(RECIPIENT.getPublicKey())
.kind(Kinds.REACTION)
.content("+")
.tags(List.of(
GenericTag.of("e", targetEventId),
GenericTag.of("p", targetAuthorPubKey)
))
.build();
RECIPIENT.sign(like);
// Emoji reaction
GenericEvent emoji = GenericEvent.builder()
.pubKey(RECIPIENT.getPublicKey())
.kind(Kinds.REACTION)
.content("\uD83D\uDD25") // fire emoji
.tags(List.of(
GenericTag.of("e", targetEventId),
GenericTag.of("p", targetAuthorPubKey)
))
.build();
RECIPIENT.sign(emoji);Purpose: Events that replace previous events of the same kind per pubkey.
// Contact list (kind 3) — only the latest is kept
GenericEvent contactList = GenericEvent.builder()
.pubKey(SENDER.getPublicKey())
.kind(Kinds.CONTACT_LIST)
.content("")
.tags(List.of(
GenericTag.of("p", friend1PubKey, "wss://relay1.example.com"),
GenericTag.of("p", friend2PubKey, "wss://relay2.example.com")
))
.build();
SENDER.sign(contactList);Purpose: Events that relays should not persist (kind 20000-29999).
GenericEvent typing = GenericEvent.builder()
.pubKey(SENDER.getPublicKey())
.kind(20001) // ephemeral range
.content("{\"typing\":true}")
.build();
SENDER.sign(typing);Purpose: Query relays for specific events.
import nostr.event.filter.EventFilter;
import nostr.event.filter.Filters;
import nostr.event.message.ReqMessage;
// Build filters
EventFilter filter = EventFilter.builder()
.kinds(List.of(Kinds.TEXT_NOTE, Kinds.REACTION))
.authors(List.of(pubKeyHex))
.since(System.currentTimeMillis() / 1000 - 86400) // last 24 hours
.limit(50)
.build();
Filters filters = new Filters(filter);
String subId = "my-sub-" + System.currentTimeMillis();
ReqMessage req = new ReqMessage(subId, filters);
// Blocking request
try (NostrRelayClient client = new NostrRelayClient(RELAY)) {
List<String> responses = client.send(req);
responses.forEach(System.out::println);
}
// Non-blocking subscription
try (NostrRelayClient client = new NostrRelayClient(RELAY)) {
AutoCloseable subscription = client.subscribe(
req,
message -> System.out.println("Event: " + message),
error -> System.err.println("Error: " + error.getMessage()),
() -> System.out.println("Closed")
);
Thread.sleep(10_000); // listen for 10 seconds
subscription.close();
}Purpose: Non-blocking relay operations using Virtual Threads.
// Connect and send asynchronously
NostrRelayClient.connectAsync(RELAY)
.thenCompose(client -> client.sendAsync(new EventMessage(event)))
.thenAccept(responses -> {
System.out.println("Sent! Responses: " + responses);
})
.exceptionally(ex -> {
System.err.println("Failed: " + ex.getMessage());
return null;
})
.join();
// Async subscription
NostrRelayClient.connectAsync(RELAY)
.thenCompose(client -> client.subscribeAsync(
req.encode(),
message -> System.out.println("Event: " + message),
error -> System.err.println("Error: " + error),
() -> System.out.println("Done")
))
.thenAccept(subscription -> {
// Close when done: subscription.close()
});- API how-to — Minimal setup and quick start
- Streaming subscriptions — Long-lived subscriptions
- Custom events — Working with custom event kinds
- Events and tags — In-depth guide to GenericEvent and GenericTag
- API reference — Full class and method reference