This tutorial provides a detailed guide on building a small microblog that utilizes the ActivityPub protocol, similar to popular platforms like Mastodon and Misskey. We will be employing Fedify, an ActivityPub server framework, focusing on practical implementation rather than delving deep into the theoretical aspects of the underlying technology.

Target Audience

This tutorial caters to individuals interested in learning about Fedify and creating their own ActivityPub server software. We assume you have experience in developing web applications with HTML and HTTP, as well as familiarity with command-line interfaces, SQL, JSON, and basic JavaScript. No prior knowledge of TypeScript, JSX, ActivityPub, or Fedify is required, as we will guide you through the necessary concepts along the way.

While you dont need experience with ActivityPub software, its beneficial to have used at least one such application, like Mastodon or Misskey, to grasp the functionality we aim to replicate.

Throughout this tutorial, we will create a single-user microblog capable of communicating with other federated applications and services via ActivityPub. The core features of this software include:

  • Ability to create only one user account.
  • Other accounts in the fediverse can follow this user.
  • Followers have the option to unfollow the user.
  • The user can view their list of followers.
  • The user can create posts visible to their followers.
  • The user can follow other accounts in the fediverse.
  • The user can view a chronological list of posts from accounts they follow.

For simplicity, we will impose certain limitations:

  • Account profiles (bio, photos, etc.) cannot be set.
  • Once created, an account cannot be deleted.
  • Posts cannot be edited or deleted once published.
  • Users cannot unfollow accounts once followed.
  • No likes, shares, or comments are permitted.
  • No search functionality will be available.
  • No security features like authentication or permission checks are included.

After completing the tutorial, you are encouraged to enhance your microblog with additional features as good practice.

The complete source code for the tutorial can be found in the GitHub repository, organized by commits for each implementation step for your convenience.

Setting Up the Development Environment

Fedify supports three JavaScript runtimes: Deno, Bun, and Node.js. Node.js is the most widely used and will be the foundation for this tutorial.

Note: A JavaScript runtime is a platform that runs JavaScript code. Web browsers are one type; for server-side applications, Node.js is the most popular. Recently, cloud edge functions like Cloudflare Workers have also gained traction.

To use Fedify, ensure that you have Node.js version 20.0.0 or higher installed. Numerous installation methods are available; choose one that fits your preferences.

Upon installing Node.js, you will have access to the node and npm commands:

node --versionnpm --version

Installing the Fedify Command

To set up a Fedify project, you need to install the Fedify command on your system. The easiest way to do this is using the npm command:

npm install -g @fedify/cli

After installation, check to confirm that you can use the Fedify command:

fedify --version

Ensure that the version number is 1.0.0 or higher; otherwise, the tutorial might not proceed as expected.

Initializing the Project

To begin a new Fedify project, lets choose a directory path for our work. In this tutorial, well name it microblog. Run the following command to initialize the project:

fedify init microblog

This command will prompt you with a series of questions; choose the following options sequentially: Node.js, npm, Hono, In-memory, and In-process.

Choose the JavaScript runtime to use Node.jsChoose the package manager to use npmChoose the web framework to integrate Fedify with HonoChoose the key-value store to use for caching In-memoryChoose the message queue to use for background jobs In-process

Fedify is not a full-stack framework but is specialized for implementing ActivityPub servers. Thus, it is designed to work alongside other web frameworks. In this tutorial, we will use Hono as our web framework.

Once initialized, you will see a directory structure created in your working directory, which includes:

  • .vscode/ Visual Studio Code related settings
  • node_modules/ Directory where dependent packages are stored
  • src/ Source code containing various files for the server logic
  • package.json Package metadata
  • tsconfig.json TypeScript settings

Note that we are utilizing TypeScript instead of JavaScript, which is evident from the presence of .ts and .tsx files in our structure.

Running the Server

To verify that everything is working correctly, navigate to the project directory and run:

npm run dev

This command will start the server, which will keep running until you interrupt it with Ctrl + C. When the server is active, you should see a message indicating its address, typically http:

Testing the Basic Setup

With the server up and running, open a new terminal tab and execute the following command to look up an actor on your newly established ActivityPub server:

fedify lookup http:

Receiving an output indicating success confirms that your server is operational:

 Looking up the object...Person { id: URL "http:

This result indicates that theres an actor object located at the path /users/john, confirming that our server is functioning as intended.

Visual Studio Code

While any text editor can be used, we recommend Visual Studio Code for this tutorial since it provides excellent support for TypeScript development. After installing, open your working directory in Visual Studio Code via the menu option File Open Folder.

If prompted to install the recommended 'Biome' extension from biomejs for this repository, click Install. This extension formats your TypeScript code automatically, easing the development process.

Understanding TypeScript

TypeScript enhances JavaScript by adding static type checking, which helps catch errors during development. Types are specified using a colon (:) followed by the type definition. For example:

let foo: string;

TypeScript also facilitates auto-completion for coding efficiency, allowing developers to write code more rapidly.

Implementing the Account Creation Page

The first step in our microblogging application is creating an account creation page. We will create a file named src/views.tsx and define a <Layout> component using JSX.

export const Layout: FC = (props) => (  <html lang="en">    <head>      <meta charset="utf-8" />      <meta name="viewport" content="width=device-width, initial-scale=1" />      <title>Microblog</title>    </head>    <body>      <main>{props.children}</main>    </body>  </html>);

Next, we will define the <SetupForm> component for account creation:

export const SetupForm: FC = () => (  <>    <h1>Set Up Your Microblog</h1>    <form method="post" action="/setup">      <fieldset>        <label>Username</label>        <input type="text" name="username" required maxlength={50} />        </label>      </fieldset>      <input type="submit" value="Setup" />    </form>  </>);

Now, lets utilize these components and display the setup form in our app:

app.get("/setup", (c) => c.html(<Layout><SetupForm /></Layout>));

After completing these tasks, opening http:

Database Setup

To store account information, we will use SQLite. In the src/schema.sql file, we will declare a table to manage user details:

CREATE TABLE IF NOT EXISTS users (  id INTEGER NOT NULL PRIMARY KEY CHECK (id = 1),  username TEXT NOT NULL UNIQUE CHECK (trim(lower(username)) = username AND username <> '' AND length(username) <= 50));

The unique constraint on the id column ensures that only one record is created in this table. To create the database file, we will execute the SQL script:

sqlite3 microblog.sqlite3 < src/schema.sql

This command will generate a microblog.sqlite3 file, which will hold all your SQLite data.

Database Connection in the App

To connect to the SQLite database in our application, we will use the better-sqlite3 library. Install the package using npm:

npm add better-sqlite3

We will also install the type definitions for TypeScript:

npm add --save-dev @types/better-sqlite3

With the packages installed, create a new file named src/db.ts to manage database connections:

import Database from 'better-sqlite3';const db = new Database('microblog.sqlite3');export default db;

This setup allows us to interact with our SQLite database seamlessly.

Inserting Records

With the database connection established, we can now insert records. In the src/app.tsx file, we will implement a POST handler for creating new accounts:

app.post('/setup', async (c) => {  const form = await c.req.formData();  const username = form.get('username');  db.prepare('INSERT INTO users (username) VALUES (?)').run(username);  return c.redirect('/');});

We will also add checks to prevent duplicate account creations and redirect users appropriately.

Profile Page

Next, we will create a profile page that displays user information and their posts. In the src/views.tsx file, define a <Profile> component:

export const Profile: FC = ({ name, handle }) => (  <>    <h1>{name}</h1>    <p>{handle}</p>  </>);

We will also add a request handler to display the user's profile when accessing /users/{username}.

Implementing ActivityPub Actor

Since ActivityPub is centered around actors, we must implement an actor management system within our application. Each action, such as creating a post or following another user, is considered an activity sent between actors. Modifying the existing src/federation.ts file will allow us to handle actor-related functionalities appropriately.

With that setup, we can now start building additional functionalities such as managing followers, sending activities, and displaying posts. As we proceed, you will gain a comprehensive understanding of how to create a federated microblog using Fedify.

Note: The complete source code and detailed explanations for each step will be available in the GitHub repository for further reference.