Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Hello, Nostr!

The first program publishes a signed text note. It covers identity, event construction, relay connection, and the publication result.

Rust
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(())
}
Python
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())
JavaScript
Node.js
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();
Web
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.

React Native
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.

Kotlin
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()
}
Swift
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.

C#
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.