Creating Your Own Federated Microblog Using Fedify
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 settingsnode_modules/
Directory where dependent packages are storedsrc/
Source code containing various files for the server logicpackage.json
Package metadatatsconfig.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 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: Receiving an output indicating success confirms that your server is operational: This result indicates that theres an actor object located at the path 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: 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 Next, we will define the Now, lets utilize these components and display the setup form in our app: After completing these tasks, opening Database Setup To store account information, we will use SQLite. In the The unique constraint on the This command will generate a Database Connection in the App To connect to the SQLite database in our application, we will use the We will also install the type definitions for TypeScript: With the packages installed, create a new file named 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 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 We will also add a request handler to display the user's profile when accessing 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 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.Ctrl + C
. When the server is active, you should see a message indicating its address, typically http:
fedify lookup http:
Looking up the object...Person { id: URL "http:
/users/john
, confirming that our server is functioning as intended.let foo: string;
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>);
<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> </>);
app.get("/setup", (c) => c.html(<Layout><SetupForm /></Layout>));
http:
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));
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
microblog.sqlite3
file, which will hold all your SQLite data.better-sqlite3
library. Install the package using npm:npm add better-sqlite3
npm add --save-dev @types/better-sqlite3
src/db.ts
to manage database connections:import Database from 'better-sqlite3';const db = new Database('microblog.sqlite3');export default db;
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('/');});
src/views.tsx
file, define a <Profile>
component:export const Profile: FC
/users/{username}
.src/federation.ts
file will allow us to handle actor-related functionalities appropriately.