Step-by-Step Guide to Send a Nostr Message
Deze inhoud is nog niet vertaald.
This guide will walk you through sending a Nostr message in different environments using JavaScript. We’ll cover how to generate keys, create and sign an event, connect to relays, and publish the message to the Nostr network using WebSockets.
Step 1: Import the Nostr Tools Library
This tutorial requires the nostr-tools
library to generate keys, create events, and interact with the Nostr network. We will use version 2.10.4
of the library. Here are the steps to import the library in different environments:
To import the Nostr Tools library in a browser environment, we need to first create an HTML file. Below is an HTML layout with a form to send a Nostr message with some basic styling.
<!DOCTYPE html><html> <head> <style> #relays:not(:empty)::before { content: "Published on:"; font-weight: bold; } </style> </head>
<body> <form id="nostr"> <input name="note" value="#hellonostr" placeholder="Enter your message" required /> <button type="submit">Send</button> </form>
<ul id="keys"></ul> <ul id="relays"></ul> </body>
<script type="module"> // Snippets below will go here </script></html>
Now, we can import the Nostr Tools library to generate keys and create events. We’ll use the esm.sh
CDN to import it directly in the browser:
Below is a guide to import the nostr-tools
library in other runtimes like Node.js/Bun (or any npm-compatible runtime), Deno (or any runtime that supports loading modules directly from URLs), or the browser’s devtools console using dynamic import.
If you are using Node.js/Bun or a similar runtime that supports loading modules from npm, you need to first install the nostr-tools
package:
Once you have installed the package, we need to create a .js
file (.mjs
for Node.js
) and import the library like this:
import * as NostrTools from "nostr-tools";
If you are using a deno or a similar runtime that supports loading modules directly from URLs, you can import the library like this without any installation:
If you want to run this code in the browser’s devtools console, since the browser doesn’t support loading ES modules outside of a <script type="module">
tag, we need to use dynamic import to load the module.
Step 2: Define the Required Variables
Let’s define the required variables that we will use in the following steps.
In the web, we need to get the required elements and listen to the form submission event. We’ll also define the message content & list of relays where we want to publish the message.
const form = document.getElementById("nostr"), keys = document.getElementById("keys"), relays = document.getElementById("relays");
const relayUrls = [ "wss://relay.damus.io", "wss://nostr.gleeze.com", "wss://frysian.nostrich.casa", "wss://nostr-01.yakihonne.com",];
form.addEventListener("submit", async (event) => { event.preventDefault();
const content = new FormData(form).get("note"); // Get the message content
// Snippets below will go here});
For any other non-browser runtimes, we are going to define the message content and list of relays where we want to publish the message.
const content = "#hellonostr";
const relayUrls = [ "wss://relay.damus.io", "wss://nostr.gleeze.com", "wss://frysian.nostrich.casa", "wss://nostr-01.yakihonne.com",];
Step 3: Key Generation
First, we generate a new private key, which can be in different formats: Uint8Array (byte array for raw data processing), hex (human-readable), or Bech32 (error-resistant, human-readable format). Here is the key generation process:
// Generate a new private key in Uint8Array formatconst privKey = NostrTools.generateSecretKey();
// Get the public key in hex format from the private keyconst pubKey = NostrTools.getPublicKey(privKey);
// Display private and public keys in Bech32 (nsec/npub) formatkeys.innerHTML = ` <li><b>Private Key</b>: ${NostrTools.nip19.nsecEncode(privKey)}</li> <li><b>Public Key: ${NostrTools.nip19.npubEncode(pubKey)}</li>`;
// Generate a new private key in Uint8Array formatconst privKey = NostrTools.generateSecretKey();
// Get the public key in hex format from the private keyconst pubKey = NostrTools.getPublicKey(privKey);
// Display private and public keys in Bech32 (nsec/npub) formatconsole.log("Private Key:", NostrTools.nip19.nsecEncode(privKey));console.log("Public Key:", NostrTools.nip19.npubEncode(pubKey));
Step 4: Create and Sign an Event
Next, we create a Kind 1 event (a note) and sign it with the private key:
// Create a new Nostr eventlet NostrEvent = { kind: 1, // Kind 1 is a text note pubkey: pubKey, created_at: Math.floor(Date.now() / 1000), // Unix timestamp in seconds tags: [], // Array of references to other events ('e'), pubkeys ('p') or addressable content ('a') content, // Your message content, defined in Step 2};
// Sign the event with the private keyNostrEvent.id = NostrTools.getEventHash(NostrEvent);NostrEvent = await NostrTools.finalizeEvent(NostrEvent, privKey);
// Log the signed eventconsole.log("Signed event:", NostrEvent);
Step 5: Connect to Relays and Send the Event
To broadcast the event to the Nostr network, we connect to relay servers via WebSockets. You can find a list of known relays at https://nostr.watch/. The relays handle the event in JSON format. Here’s how we send the event:
// Clear previous relay listrelays.innerHTML = "";
// Display the relays where the message was publishedfunction displayRelays(contentHTML) { relays.insertAdjacentHTML("beforeend", contentHTML);}
relayUrls.forEach((url) => { const socket = new WebSocket(url);
socket.onopen = () => { console.log(`Connected to ${url}`);
// Send the signed event to the relay in JSON format socket.send(JSON.stringify(["EVENT", NostrEvent])); };
socket.onmessage = (message) => { console.log(`Message from ${url}:`, message.data);
displayRelays(`<li>${url}</li>`); // Display relay URL };
socket.onclose = () => { console.log(`Disconnected from ${url}`); };});
// Clear the formform.reset();
relayUrls.forEach((url) => { const socket = new WebSocket(url);
socket.onopen = () => { console.log(`Connected to ${url}`);
// Send the signed event to the relay in JSON format socket.send(JSON.stringify(["EVENT", NostrEvent])); };
socket.onmessage = (message) => { console.log(`Message from ${url}:`, message.data);
// Display relay URL console.log("Published on:", url); };
socket.onclose = () => { console.log(`Disconnected from ${url}`); };});
Additional Relay Information
-
Why use multiple relays? Using multiple relays increases the likelihood that your content will be distributed and stay accessible, even if some relays go offline. Relays operate independently, meaning they don’t automatically sync data with each other. Connecting to several relays ensures broader distribution of your messages and improves redundancy.
-
Free vs. paid relays: There are both free and paid relays in the Nostr ecosystem. Free relays are great for testing and learning, while paid relays typically offer better performance and reliability because they can afford to maintain higher-quality infrastructure.
-
Using relays based on follower lists: Some Nostr clients dynamically select relays based on the user’s follower list. This ensures that you’re connected to the same relays as the people you follow, improving message delivery and visibility.
Summary of the Process
To recap, here’s the process for sending a Nostr message:
-
Key Generation: We generated a private/public key pair using Nostr tools.
-
Creating and Signing an Event: We created a Kind 1 event (a note) and signed it with the private key.
-
Connecting to Relays: We connected to multiple relay servers using WebSockets to ensure the message was distributed across the Nostr network.
-
Displaying Published Relays: We displayed the relays where the message was successfully published.
By following this guide, you’ve successfully sent a Nostr message using HTML, JavaScript, and WebSockets. This provides a foundation for understanding how to interact with the Nostr protocol and work with its key features.