Skip to content

Instantly share code, notes, and snippets.

@tigra
Last active April 2, 2025 17:24
How to post to Moera using Typescript moeralib library

Building a TypeScript Application with Moera API: A Complete Guide

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.

Prerequisites

  • 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)

What is Moera?

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

Getting Started

Step 1: Set Up Your Development Environment

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

Step 2: Install Required Packages

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

Step 3: Create TypeScript Configuration

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"
  }
}

Step 4: Create an Admin Token on Your Moera Node

Before writing code, you need to create an admin token with appropriate permissions:

  1. 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)
  2. Navigate to Settings (gear icon) → My blog → Applications
  3. Click the "Create token" button
  4. Give your token a name (e.g., "API Access Token") so you can identify it later if you need to revoke it
  5. 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)
  6. Click "Create" and copy the generated token value
  7. 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.

Step 5: Create a Basic Script

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();

Step 6: Add a Script to package.json

Edit your package.json file to add a script for running your application:

"scripts": {
  "start": "ts-node src/index.ts"
}

Step 7: Run Your Application

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.

Building a More Advanced Application

Now let's create a more complete application that demonstrates more features:

  1. Resolving a node by name
  2. Creating a post with specific timestamps
  3. Publishing to specific feeds
  4. 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.

Understanding the Code

Let's dive deeper into key parts of our code:

Authentication

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.

Creating a Post

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 containing subject and text 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 the update-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.

Setting Publication Timestamp

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

Node Resolution

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

Common Issues and Solutions

Authentication Errors

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

Schema Validation Errors

Errors like "Request syntax is invalid" or "Body text encoding is incorrect":

  • Make sure you're using JSON.stringify() for the bodySrc field typescript-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

Node Resolution Failures

If resolve() returns null:

  • Verify the name is correct and registered
  • Check your internet connection
  • The naming server might be temporarily unavailable

Important Concepts in Moera

Understanding these concepts will help you work with the Moera API more effectively:

Decentralization

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

Content Structure

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

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

Next Steps

Now that you have a working application, here are some ways to extend it:

  1. Add comment functionality: Implement creating and retrieving comments on posts
  2. Implement reactions: Add the ability to react to posts with emojis
  3. Build a timeline viewer: Create a tool that fetches and displays the user's timeline
  4. Create a media uploader: Extend your app to upload and attach images to posts
  5. Study Moera Architecture Overview
  6. Study Moera Developer's Guide

Conclusion

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!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment