Hello, Nostr!
The first program publishes a signed text note. It covers identity, event construction, relay connection, and the publication result.
use nostr_sdk::prelude::*;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let keys = Keys::generate();
let client = Client::default();
client.add_relay("wss://relay.damus.io").await?;
client.connect().await;
let event = EventBuilder::new(Kind::TextNote, "Hello from Nostr Dev Kit!").finalize(&keys)?;
let output = client.send_event(&event).await?;
println!("Published {} to {:?}", output.id(), output.success);
Ok(())
}
import asyncio
from nostr_sdk import (
Client,
EventBuilder,
Kind,
KindStandard,
Keys,
RelayUrl,
)
async def main() -> None:
keys = Keys.generate()
client = Client()
relay = RelayUrl.parse("wss://relay.damus.io")
await client.add_relay(relay)
await client.connect()
event = EventBuilder(
Kind.from_std(KindStandard.TEXT_NOTE), "Hello from Nostr Dev Kit!"
).finalize(keys)
output = await client.send_event(event)
print(f"Published {output.id.to_bech32()} to {output.success}")
if __name__ == "__main__":
asyncio.run(main())
import {
Client,
EventBuilder,
Kind,
KindStandard,
Keys,
RelayUrl,
} from "@nostrdevkit/nostr-sdk-node";
async function main() {
const keys = Keys.generate();
const client = new Client();
const relay = RelayUrl.parse("wss://relay.damus.io");
await client.addRelay(relay);
await client.connect();
const event = new EventBuilder(
Kind.fromStd(KindStandard.TextNote),
"Hello from Nostr Dev Kit!",
).finalize(keys);
const output = await client.sendEvent(event);
console.log(`Published ${output.id.toBech32()} to`, output.success);
}
await main();
import {
Client,
EventBuilder,
Kind,
KindStandard,
Keys,
RelayUrl,
uniffiInitAsync,
} from "@nostrdevkit/nostr-sdk-web";
async function main() {
await uniffiInitAsync();
const keys = Keys.generate();
const client = new Client();
await client.addRelay(RelayUrl.parse("wss://relay.damus.io"));
await client.connect();
const event = new EventBuilder(
Kind.fromStd(KindStandard.TextNote),
"Hello from Nostr Dev Kit!",
).finalize(keys);
const output = await client.sendEvent(event);
console.log(`Published ${output.id.toBech32()} to`, output.success);
}
await main();
The Web package must finish uniffiInitAsync() before any generated SDK type is used.
import {
Client,
EventBuilder,
Kind,
KindStandard,
Keys,
RelayUrl,
} from "@nostrdevkit/nostr-sdk-react-native";
export async function publishHello() {
const keys = Keys.generate();
const client = new Client();
await client.addRelay(RelayUrl.parse("wss://relay.damus.io"));
await client.connect();
const event = new EventBuilder(
Kind.fromStd(KindStandard.TextNote),
"Hello from Nostr Dev Kit!",
).finalize(keys);
const output = await client.sendEvent(event);
console.log(`Published ${output.id.toBech32()} to`, output.success);
}
Call publishHello from an application task; React Native applications do not use top-level await.
package rust.nostr.snippets
import kotlinx.coroutines.runBlocking
import org.nostrdevkit.sdk.*
suspend fun publishHello() {
val keys = Keys.generate()
val client = Client()
val relay = RelayUrl.parse("wss://relay.damus.io")
client.addRelay(relay)
client.connect()
val event = EventBuilder(
Kind.fromStd(KindStandard.TEXT_NOTE),
"Hello from Nostr Dev Kit!",
).finalize(keys)
val output = client.sendEvent(event)
println("Published ${output.id.toBech32()} to ${output.success}")
}
fun main() = runBlocking {
publishHello()
}
import Foundation
import NostrSDK
func publishHello() async throws {
let keys = Keys.generate()
let client = Client()
let relay = try RelayUrl.parse(url: "wss://relay.damus.io")
_ = try await client.addRelay(url: relay)
await client.connect()
let event = try EventBuilder(
kind: Kind.fromStd(e: .textNote),
content: "Hello from Nostr Dev Kit!"
).finalize(signer: keys)
let output = try await client.sendEvent(event: event)
print("Published \(try output.id.toBech32()) to \(output.success)")
}
Call publishHello() from an asynchronous application entry point.
namespace Snippets;
using Nostr.Sdk;
public static class ClientExample
{
public static async Task PublishHello()
{
var keys = Keys.Generate();
var client = new Client();
var relay = RelayUrl.Parse("wss://relay.damus.io");
await client.AddRelay(relay);
await client.Connect();
var @event = new EventBuilder(
Kind.FromStd(KindStandard.TextNote),
"Hello from Nostr Dev Kit!"
).Finalize(keys);
var output = await client.SendEvent(@event);
Console.WriteLine($"Published {output.Id.ToBech32()} to {string.Join(", ", output.Success)}");
}
}
Call ClientExample.PublishHello() from an asynchronous entry point.
Read the program
Every version performs the same four operations.
1. Create a temporary identity
Keys.generate creates a fresh key pair. Running the example again produces another author.
2. Construct the client
Client creates a relay pool with in-memory storage. The same instance is used to connect and publish.
3. Register and connect the relay
Adding the URL registers the relay; connect starts its managed connection work.
The example uses wss://relay.damus.io only as an accessible tutorial endpoint. Public relay uptime and write policy
are outside the SDK’s control, so a rejection or timeout does not necessarily mean the program is assembled
incorrectly.
4. Build, sign, and publish
The builder creates a NIP-01 text note.
send_event and its language equivalents wait for relay acknowledgements and return both the event ID and the relays
that accepted it. Inspect this output when delivery guarantees matter; a successful call does not mean that every
configured relay accepted the event.
The output has this shape; the actual ID and relay set vary on every run:
Published note1... to [wss://relay.damus.io/]
No explicit shutdown call is required. When the client is dropped, its relay connections and background tasks are stopped automatically.
The next three chapters take apart the values used here: keys and signers, events, and filters.