This tutorial will walk you through creating a TypeScript application that interacts with the Moera decentralized social network. You'll learn how to authenticate with a Moera node, create posts, and retrieve post content using the moeralib
library.
- Basic programming experience (any language). Don't worry if you haven't worked with Typescript or Javascript yet!
- A Moera account with a node (you'll need access to its admin area)
- A computer with internet access
- Node.js installed (version 18.10.0 or higher)
Moera is a decentralized social network where users control their own data through personal "nodes." Unlike centralized networks, Moera is distributed across many servers owned by different individuals or organizations. Each user has a unique name that can be used to find them across the network.
The main concepts to understand:
- Node: Your personal server that stores your content
- Naming: The system that maps user-friendly names to node addresses
- Cartes: Authentication tokens for cross-node operations
First, let's verify your Node.js installation and create a new project:
# Check if Node.js is already installed
node --version
# Should show v18.10.0 or higher
# If not installed, download and install from https://nodejs.org/
# Create a new project directory
mkdir moera-posting-app
cd moera-posting-app
# Initialize a new Node.js project
npm init -y
Now let's install TypeScript and the Moera library:
# Install TypeScript and related tools
npm install --save-dev typescript ts-node @types/node
# Install the Moera library
npm install moeralib
Create a TypeScript configuration file:
# Create tsconfig.json
npx tsc --init
Edit the generated tsconfig.json
file to include these settings:
{
"compilerOptions": {
"target": "es2016",
"module": "commonjs",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true,
"outDir": "./dist"
}
}
Before writing code, you need to create an admin token with appropriate permissions:
- Log in to your Moera web client (this could be at
https://yourname.moera.blog
, or any other URL as configured in the Moera naming system) - Navigate to Settings (gear icon) → My blog → Applications
- Click the "Create token" button
- Give your token a name (e.g., "API Access Token") so you can identify it later if you need to revoke it
- Set the following permissions:
- "Create posts signed by your name" (
add-post
internally) - "Add and modify stories in feeds" (
update-feeds
internally) - this is required to publish posts to your timeline - "View any content (posts, comments, reactions), except media files" (
view-content
internally)
- "Create posts signed by your name" (
- Click "Create" and copy the generated token value
- Important: Save this token securely - it provides administrative access to your node
Note: There are other permissions available if you plan to extend your application's functionality later.
Create a file named src/index.ts
with the following content:
const { MoeraNode } = require('moeralib/node');
const util = require('util');
// Configuration - update these values with your information
const NODE_URL = 'https://yournode.com'; // Your Moera node URL
const ADMIN_TOKEN = 'your-admin-token'; // The token you created
async function main() {
try {
// Initialize the Moera node client
const node = new MoeraNode(NODE_URL);
// Configure authentication with admin token
node.token(ADMIN_TOKEN);
node.authAdmin();
console.log('Node configured for authentication - will be used in API calls');
// Create a simple post
const postingData = {
bodySrc: {
subject: "Hello Moera world!",
text: "This is my first post created via API."
},
feedName: "timeline" // Publish to timeline feed
};
// Create the post
const newPost = await node.createPosting(postingData);
console.log('Post created successfully!');
console.log('Post ID:', newPost.id);
// Retrieve the post we just created
console.log(`\nRetrieving post with ID: ${newPost.id}`);
const retrievedPost = await node.getPosting(newPost.id);
console.log('Post retrieved successfully!');
console.log('Post subject:', retrievedPost.body.subject);
console.log('Post content:', retrievedPost.body.text);
} catch (error) {
console.error('Error:', error);
if (error && typeof error === 'object' && 'errorCode' in error) {
console.error('Error code:', error.errorCode);
}
}
}
main();
Edit your package.json
file to add a script for running your application:
"scripts": {
"start": "ts-node src/index.ts"
}
Now you can run your application:
npm start
If everything is set up correctly, you should see output similar to this:
user@macbook-pro moera-posting-demo % npm start
> moera-posting-demo@1.0.0 start
> ts-node src/index.ts
Node configured for authentication - will be used in API calls
Post created successfully!
Post ID: ab123456-7890-1234-5678-9abcdef12345
Retrieving post with ID: ab123456-7890-1234-5678-9abcdef12345
Post retrieved successfully!
Post subject: Hello Moera world!
Post content: This is my first post created via API.
Note that the post you created won't appear in your timeline yet because we haven't specified the publications
parameter. We'll address this in the advanced example.
Now let's create a more complete application that demonstrates more features:
- Resolving a node by name
- Creating a post with specific timestamps
- Publishing to specific feeds
- Retrieving and displaying the full post details
Create a new file named src/advanced.ts
:
const { MoeraNode } = require('moeralib/node');
const { MoeraNaming, resolve } = require('moeralib/naming');
const util = require('util');
// Configuration - update these values
const NODE_NAME = 'your-name'; // Your Moera name (e.g., 'john' or 'jane.0')
const ADMIN_TOKEN = 'your-admin-token'; // Your admin token
async function main() {
try {
// Step 1: Resolve the node URL from the name
console.log(`Resolving node URL for name: ${NODE_NAME}`);
const nodeUrl = await resolve(NODE_NAME);
if (!nodeUrl) {
throw new Error(`Could not resolve node for name: ${NODE_NAME}`);
}
console.log(`Node resolved to URL: ${nodeUrl}`);
// Step 2: Initialize and authenticate with the node
const node = new MoeraNode(nodeUrl);
node.token(ADMIN_TOKEN);
node.authAdmin();
console.log('Node configured for authentication - will be used in API calls');
// Step 3: Create a post with specific attributes
// Get current timestamp in seconds
const currentTime = Math.floor(Date.now() / 1000);
console.log('Creating new post with timestamp:', new Date(currentTime * 1000).toISOString());
const postingData = {
bodySrc: {
subject: "Advanced Moera API Example",
text: `This post was created programmatically at ${new Date(currentTime * 1000).toLocaleString()}.
It demonstrates how to use the Moera API with specific timestamps and feed settings.`
},
createdAt: currentTime, // Set creation time
// Publish to timeline feed with timestamp
publications: [
{
feedName: "timeline",
publishedAt: currentTime
}
],
feedName: "timeline"
};
// Create the post
const newPost = await node.createPosting(postingData);
console.log('Post created successfully!');
console.log('Post ID:', newPost.id);
// Step 4: Wait briefly for the post to be processed
console.log('Waiting for post to be processed...');
await new Promise(resolve => setTimeout(resolve, 1000));
// Step 5: Retrieve and display the post
console.log(`\nRetrieving post with ID: ${newPost.id}`);
const retrievedPost = await node.getPosting(newPost.id);
console.log('\nPost retrieved successfully!');
console.log('\n--- Post Summary ---');
console.log(`Title: ${retrievedPost.heading}`);
console.log(`Created: ${new Date(retrievedPost.createdAt * 1000).toLocaleString()}`);
console.log(`Owner: ${retrievedPost.ownerFullName} (${retrievedPost.ownerName})`);
console.log(`Content: ${retrievedPost.body.text.replace(/<[^>]*>/g, '')}`);
console.log('\n--- Full Post Details ---');
console.log(util.inspect(retrievedPost, { depth: 2, colors: true }));
} catch (error) {
console.error('Error:', error);
if (error && typeof error === 'object' && 'errorCode' in error) {
console.error('Error code:', error.errorCode);
}
}
}
main();
Add this script to your package.json
:
"scripts": {
"start": "ts-node src/index.ts",
"advanced": "ts-node src/advanced.ts"
}
Run the advanced script:
npm run advanced
You should see output similar to this:
user@macbook-pro moera-posting-demo % npm run advanced
> moera-posting-demo@1.0.0 advanced
> ts-node src/adv.ts
Resolving node URL for name: username
Node resolved to URL: https://username.moera.blog/moera
Node configured for authentication - will be used in API calls
Creating new post with timestamp: 2025-03-17T18:47:58.000Z
Post created successfully!
Post ID: ef987654-3210-abcd-5678-fedcba09876
Retrieving post with ID: ef987654-3210-abcd-5678-fedcba09876
Post retrieved successfully!
--- Post Summary ---
Title: Advanced Moera API Example
Created: 3/17/2025, 7:47:58 PM
Owner: Username (username_0)
Content: This post was created programmatically at 3/17/2025, 7:47:58 PM.It demonstrates how to use the Moera API with specific timestamps and feed settings.
--- Full Post Details ---
{
id: 'ef987654-3210-abcd-5678-fedcba09876',
revisionId: '12ab3456-78cd-90ef-ab12-cdef34567890',
totalRevisions: 1,
ownerName: 'username_0',
ownerFullName: 'Username',
ownerGender: 'Male',
ownerAvatar: {
mediaId: 'Aa1BbCc2DdEe3FfGg4HhIi5JjKk',
path: 'public/Aa1BbCc2DdEe3FfGg4HhIi5JjKk.jpg',
width: 200,
height: 200,
shape: 'square'
},
bodyPreview: {},
bodySrcHash: 'Ab3Cd6Ef9Gh1Ij4Kl7Mn0Pq3Rs6Tv9Wx2Yz5',
bodySrcFormat: 'plain-text',
body: {
subject: 'Advanced Moera API Example',
text: '<p>This post was created programmatically at 3/17/2025, 7:47:58 PM.<br/>It demonstrates how to use the Moera API with specific timestamps and feed settings.</p>'
},
bodyFormat: 'message',
media: [],
heading: 'Advanced Moera API Example',
createdAt: 1742237278,
editedAt: 1742237278,
revisionCreatedAt: 1742237278,
digest: 'Aa1Bb2Cc3Dd4Ee5Ff6Gg7Hh8Ii9Jj0Kk1Ll2Mm3Nn',
signature: 'Ab3Cd6Ef9Gh1Ij4Kl7Mn0Pq3Rs6Tv9Wx2Yz5Ab3Cd6Ef9Gh1Ij4Kl7Mn0Pq3Rs6Tv9Wx2Yz5',
signatureVersion: 1,
feedReferences: [
{
feedName: 'timeline',
publishedAt: 1742237278,
pinned: false,
moment: 1742237278174,
storyId: '12345678-9abc-def0-1234-56789abcdef0',
operations: [Object]
}
],
operations: {},
commentOperations: {},
reactionOperations: {},
commentReactionOperations: {},
sheriffs: [ 'moderation-sheriff_0' ],
acceptedReactions: { positive: '', negative: '' },
reactions: {
entryId: 'ef987654-3210-abcd-5678-fedcba09876',
positive: [],
negative: []
},
sources: [],
totalComments: 0
}
Note that this post will appear in your timeline feed because we've specified the publications
parameter with the correct timestamp.
Let's dive deeper into key parts of our code:
node.token(ADMIN_TOKEN);
node.authAdmin();
This sets up admin authentication using your token. The moeralib
library uses this token to authenticate all API requests to your node.
const postingData = {
bodySrc: {
subject: "Hello Moera world!",
text: "<p>This is my first post created via API.</p>"
},
feedName: "timeline"
};
const newPost = await node.createPosting(postingData);
Important points:
- The
bodySrc
field must be a JSON string containingsubject
andtext
fields - The
text
field is treated as plain text by default - The
feedName
specifies where to publish the post (usually "timeline") - Without setting the
publications
array, the post won't appear in your timeline feed - that's why theupdate-feeds
permission is important
Security Note: While we hardcode the token in the tutorial for simplicity, in a production environment you should NEVER hardcode authentication tokens. Store them in environment variables or a secure configuration system, and ensure they are not committed to version control repositories or shared in code samples.
const currentTime = Math.floor(Date.now() / 1000);
publications: [
{
feedName: "timeline",
publishedAt: currentTime
}
]
This sets the publication time for the post in the timeline feed:
Date.now()
returns milliseconds since Jan 1, 1970- Dividing by 1000 converts to seconds (the format Moera expects)
Math.floor()
rounds down to the nearest integer
const nodeUrl = await resolve(NODE_NAME);
This uses Moera's naming system to convert a user-friendly name like "john" to the actual node URL. This is important because:
- Node URLs might change over time
- Names are easier for humans to remember than URLs
- The naming system helps nodes find each other, which is how Moera maintains a decentralized yet discoverable network
If you see "Authentication required"
or "Authentication token is invalid"
:
- Double-check that your token is correct
- Ensure your token has the necessary permissions
- Verify that you're using
authAdmin()
after setting the token
Errors like "Request syntax is invalid"
or "Body text encoding is incorrect"
:
Make sure you're usingJSON.stringify()
for thebodySrc
fieldtypescript-moeralib
does this automatically for you now- Verify the structure of your post data matches what the API expects
- Check that all required fields are present
If resolve()
returns null:
- Verify the name is correct and registered
- Check your internet connection
- The naming server might be temporarily unavailable
Understanding these concepts will help you work with the Moera API more effectively:
Moera is decentralized, meaning there's no single central server. Instead:
- Each user runs their own node or uses a hosting provider
- The naming system helps users find each other
- Authentication is handled via tokens and cryptographic keys
Moera organizes content hierarchically:
- Postings: Top-level content items (like blog posts or status updates)
- Comments: Responses to postings or other comments
- Reactions: Quick responses (like likes or emojis) to postings or comments
Feeds organize content for viewing:
- Timeline: The main feed showing posts in chronological order
- News: Posts from other nodes that you follow
- Custom feeds: Users can create additional feeds for specific purposes
Now that you have a working application, here are some ways to extend it:
- Add comment functionality: Implement creating and retrieving comments on posts
- Implement reactions: Add the ability to react to posts with emojis
- Build a timeline viewer: Create a tool that fetches and displays the user's timeline
- Create a media uploader: Extend your app to upload and attach images to posts
- Study Moera Architecture Overview
- Study Moera Developer's Guide
You've now created a Node.js application that can authenticate with a Moera node, create posts with specific attributes, and retrieve post content. This foundation can be extended to build more complex applications for interacting with the Moera network.
The decentralized nature of Moera offers unique advantages for privacy and data ownership, and with the moeralib
library, it's relatively straightforward to build applications that leverage these benefits.
Happy coding!