Learn how to create a new topic and submit your first message on Hedera testnet using the JavaScript, Java, or Go SDK. A topic on the Hedera Consensus Service (HCS) is like a public channel: anyone who knows the topic ID can publish timestamped messages, and anyone can subscribe to the stream from a mirror node.
Prerequisites
A Hedera testnet operator account ID and DER-encoded private key (from the Quickstart)y (from the Quickstart).
A small amount of testnet HBAR (ℏ) to cover the fees
Topic creation: ≈ $0.01
Each message: < $0.001
Note
You can always check the "✅ Code Check" section at the bottom of each page to view the entire code if you run into issues. You can also post your issue to the respective SDK channel in our Discord community here.
Install the SDK
Open your terminal and create a directory hedera-examples directory. Then change into the newly created directory:
mkdirhedera-examples&&cdhedera-examples
Initialize a node.js project in this new directory:
npminit-y
Ensure you have Node.jsv18 or later installed on your machine. Then, install the JavaScript SDK.
npminstall--save@hashgraph/sdk
Update your package.json file to enable ES6 modules and configure the project:
Load your operator credentials from environment variables and configure your Hedera testnet client. The client manages your connection to the Hedera network and uses your operator account to sign transactions and pay fees.
// Load your operator credentials
const operatorId = process.env.OPERATOR_ID;
const operatorKey = process.env.OPERATOR_KEY;
// Initialize your testnet client and set operator
const client = Client.forTestnet()
.setOperator(operatorId, operatorKey);
// Load your operator credentials
AccountId operatorId = AccountId.fromString(System.getenv("OPERATOR_ID"));
PrivateKey operatorKey = PrivateKey.fromString(System.getenv("OPERATOR_KEY"));
// Initialize your testnet client and set operator
Client client = Client.forTestnet().setOperator(operatorId, operatorKey);
// Load your operator credentials
operatorId, _ := hedera.AccountIDFromString(os.Getenv("OPERATOR_ID"))
operatorKey, _ := hedera.PrivateKeyFromString(os.Getenv("OPERATOR_KEY"))
// Initialize the client for testnet
client := hedera.ClientForTestnet()
client.SetOperator(operatorId, operatorKey)
Step 2: Create a new topic
TopicCreateTransaction builds and sends the transaction to register a new HCS topic with the provided memo, and once it reaches consensus you retrieve the transaction receipt to extract the new topicId.
Why messages?
Messages on HCS are consensus-timestamped and immutable. Once published, they become part of the permanent record that anyone can verify and subscribe to in real-time.
// Build and send the transaction
const txResponse = await new TopicCreateTransaction()
.setTopicMemo("My first HCS topic") // optional description
.execute(client);
const receipt = await txResponse.getReceipt(client);
const topicId = receipt.topicId;
console.log(`Topic created: ${topicId.toString()}`);
// Build and send the create‐topic transaction
TransactionResponse txResponse = new TopicCreateTransaction()
.setTopicMemo("My first HCS topic") // optional description
.execute(client);
// Fetch the receipt to get the new topic ID
TransactionReceipt receipt = txResponse.getReceipt(client);
TopicId topicId = receipt.topicId;
System.out.println("Topic created: " + topicId);
// Build and send the create‐topic transaction
transaction := hedera.NewTopicCreateTransaction().
SetTopicMemo("My first HCS topic")
txResponse, err := transaction.Execute(client)
if err != nil {
panic(err)
}
receipt, err := txResponse.GetReceipt(client)
if err != nil {
panic(err)
}
topicID := *receipt.TopicID
fmt.Printf("Topic created: %s\n", topicID.String())
What just happened?
The SDK built a TopicCreateTransaction.
Your operator key signed and paid the fee.
Hedera nodes reached consensus and returned a unique topic ID (shard.realm.num).
Step 3: Submit Message to Topic
TopicMessageSubmitTransaction constructs and sends a transaction that submits your payload (string, bytes, or Uint8Array) as a message to a specified HCS topic. Once it reaches consensus, the message becomes part of that topic’s immutable record.
// Build & execute the message submission transaction
const message = "Hello, Hedera! \n";
const messageTransaction = new TopicMessageSubmitTransaction()
.setTopicId(topicId)
.setMessage(message);
await messageTransaction.execute(client);
console.log(`Message submitted to topic: ${message}\n`);
Messages can be up to 1 KiB each. Larger payloads must be chunked automatically by the SDK or split manually
Step 4: Query Messages Using Mirror Node API
Use the Mirror Node REST API to verify your message was published to the topic. Mirror nodes provide free access to network data without transaction fees. Mirror nodes stream every consensus-timestamped message in order, letting your app react in real time.
API endpoint:
/api/v1/topics/{topicId}/messages
Replace the placeholder:
{topicId} - Your topic ID from the creation transaction
Why this endpoint?
This endpoint retrieves all messages published to a specific topic, ordered by consensus timestamp. It returns detailed information including message content, timestamp, and sequence number, making it ideal for verifying your message was published successfully.