--- url: /intro.md --- # What is Fedify? Fedify is a TypeScript library for building federated server apps powered by [ActivityPub] and other standards, so-called [fediverse].\[^1] It aims to eliminate the complexity and boilerplate code when building a federated server app, so that you can focus on your business logic and user experience. Currently, Fedify provides the following features out of the box: * Type-safe objects for [Activity Vocabulary] (including some vendor-specific extensions) * [WebFinger] client and server * [HTTP Signatures] & [HTTP Message Signatures] * [Object Integrity Proofs][FEP-8b32] & [Linked Data Signatures] * Middlewares for handling webhooks * [NodeInfo] protocol * Enhanced interoperability with Mastodon and other popular fediverse software * [Integration with various web frameworks](./manual/integration.md) * [CLI toolchain for testing and debugging](./cli.md) If you want to know more about the project, please take a look at the following resources: * [GitHub] * Tutorials: [Learning the basics](./tutorial/basics.md) & [Creating a microblog](./tutorial/microblog.md) * [API reference] * [Examples] If you have any questions, suggestions, or feedback, please feel free to join our [Matrix chat space] or [GitHub Discussions]. \[^1]: You may already know some of the networks in the fediverse, such as [Mastodon], [Lemmy], [Pixelfed], [PeerTube], and so on. [ActivityPub]: https://www.w3.org/TR/activitypub/ [fediverse]: https://en.wikipedia.org/wiki/Fediverse [Activity Vocabulary]: https://www.w3.org/TR/activitystreams-vocabulary/ [WebFinger]: https://datatracker.ietf.org/doc/html/rfc7033 [HTTP Signatures]: https://tools.ietf.org/html/draft-cavage-http-signatures-12 [HTTP Message Signatures]: https://www.rfc-editor.org/rfc/rfc9421 [FEP-8b32]: https://w3id.org/fep/8b32 [Linked Data Signatures]: https://web.archive.org/web/20170923124140/https://w3c-dvcg.github.io/ld-signatures/ [NodeInfo]: https://nodeinfo.diaspora.software/ [GitHub]: https://github.com/fedify-dev/fedify [API reference]: https://jsr.io/@fedify/fedify [Examples]: https://github.com/fedify-dev/fedify/tree/main/examples [Matrix chat space]: https://matrix.to/#/#fedify:matrix.org [GitHub Discussions]: https://github.com/fedify-dev/fedify/discussions [Mastodon]: https://joinmastodon.org/ [Lemmy]: https://join-lemmy.org/ [Pixelfed]: https://pixelfed.org/ [PeerTube]: https://joinpeertube.org/ --- --- url: /why.md description: >- This document explains why you should consider using Fedify for your ActivityPub server development. --- # Why Fedify? Developing a federated application using ActivityPub can be complex and time-consuming. While the ActivityPub specification provides a solid foundation for decentralized social networking, implementing it from scratch presents significant challenges. Fedify alleviates these pain points, allowing developers to focus on their application's unique features rather than protocol intricacies. ## Common pain points when implementing ActivityPub from scratch Without a framework like Fedify, developers face numerous challenges: ### Technical complexity Steep learning curve : The ActivityPub family of specifications is extensive, covering hundreds of pages of technical documentation JSON-LD complexities : Dealing with context resolution, expansion, and compaction manually is error-prone Cryptographic hurdles : Implementing HTTP Signatures, key management, and signature verification correctly requires specialized knowledge ### Infrastructure requirements Message delivery reliability : Building retry logic, exponential backoff, and failure handling from scratch Performance bottlenecks : Naive message delivery implementations can cause timeouts, high memory usage, and server crashes Scalability issues : Direct delivery to thousands of followers can overload your application servers ### Federation challenges Incompatible implementations : Different ActivityPub servers interpret the specifications differently, requiring implementation-specific adaptations Evolving ecosystem : Federation standards continue to evolve with community extensions and conventions Security vulnerabilities : Potential for SSRF attacks, signature forgery, and other security issues without careful implementation ### Development overhead No standardized tools : Developers must build their own debugging and testing utilities for federation Reinventing the wheel : Common patterns like WebFinger resolution or follower management must be reimplemented Maintenance burden : Keeping up with security patches and protocol changes across the fediverse ## Protocol complexity and data handling The ActivityPub family of W3C recommendations—including ActivityStreams 2.0, Activity Vocabulary, and ActivityPub—is extensive and challenging to implement correctly. Messages encoded in JSON-LD using the ActivityStreams vocabulary require careful attention to contexts, IRI resolution, and object serialization. Fedify provides: * A higher-level abstraction over these protocols * Type-safe TypeScript classes for all standard ActivityStreams types * Utilities for creation, manipulation, and validation of ActivityPub objects ## Federation infrastructure Building an ActivityPub server requires implementing several interconnected systems: ### Message processing and delivery Fedify offers a comprehensive solution for managing ActivityPub's inbox/outbox model: * Robust inbox handlers with activity de-duplication * Configurable message queues with reliable retry mechanisms ### Scalability with fan-out architecture One of the most challenging aspects of ActivityPub implementation is efficiently delivering activities to potentially thousands of recipients. Fedify's [two-stage delivery process](./manual/send.md#optimizing-activity-delivery-for-large-audiences) addresses this: 1. For activities with many recipients, a single consolidated message containing the activity and all recipient information is created 2. A background worker processes this message and re-enqueues individual delivery tasks 3. Each delivery has independent retry logic and error handling ```mermaid flowchart TB A[Client] -->|Create post| B[Application] B -->|Create activity| C[Fedify] subgraph "Two-stage delivery process" C -->|1\. Single consolidated message| D[(Message Queue)] D -->|2\. Process message| E[Fan-out Worker] E -->|3\. Create individual delivery tasks| F[(Delivery Queue)] end F -->|Deliver to recipient 1| G[Remote server 1] F -->|Deliver to recipient 2| H[Remote server 2] F -->|Deliver to recipient N| I[Remote server N] G -.-|Failed? Retry with backoff| F H -.-|Failed? Retry with backoff| F I -.-|Failed? Retry with backoff| F ``` Benefits include: * Faster API response times for improved UX * Reduced memory consumption through payload deduplication * Configurable fan-out strategies with `"auto"`, `"skip"`, or `"force"` options ## Security and interoperability Security is paramount in federation, while interoperability across diverse implementations presents ongoing challenges. Fedify provides: * Comprehensive cryptographic support (RSA-PKCS#1-v1.5 and Ed25519 keys) * Multiple authentication methods ([HTTP Signatures](./manual/send.md#http-signatures), [HTTP Message Signatures](./manual/send.md#http-message-signatures), [Object Integrity Proofs](./manual/send.md#object-integrity-proofs), [Linked Data Signatures](./manual/send.md#linked-data-signatures)) * SSRF protection and other security best practices * [Activity transformers](./manual/send.md#activity-transformers) that adjust outgoing activities for maximum compatibility * Multi-origin support with canonical URL handling for consistent identity ## Technology agnosticism Fedify doesn't force you to use specific technologies, giving you the freedom to build with your preferred tools. ### Framework flexibility Works with virtually any JavaScript/TypeScript web framework: * [Express](./manual/integration.md#express) * [Hono](./manual/integration.md#hono) * [Fresh](./manual/integration.md#fresh) * [SvelteKit](./manual/integration.md#sveltekit) * [Next.js](./manual/integration.md#next.js) * [h3](./manual/integration.md#h3)/Nitro … and many more! ### Database independence * Use any SQL or NoSQL database (PostgreSQL, MySQL, MongoDB, etc.) * Use any ORM (Prisma, TypeORM, Drizzle ORM, etc.) * Fedify only requires a [key–value interface](./manual/kv.md) for its internal caching ## Developer experience Fedify significantly improves the developer experience through: TypeScript-native design : Comprehensive type definitions with intelligent auto-completion Observability : [Built-in OpenTelemetry support](./manual/opentelemetry.md) for tracing, metrics, and monitoring [CLI toolchain](./cli.md) : Tools for testing and debugging federation, including: ``` - [Object lookup utility](./cli.md#fedify-lookup-looking-up-an-activitypub-object) - [Ephemeral inbox server](./cli.md#fedify-inbox-ephemeral-inbox-server) for testing outgoing activities - [Local tunnel](./cli.md#fedify-tunnel-exposing-a-local-http-server-to-the-public-internet) for exposing development servers to the internet ``` ## Success stories Several notable projects have chosen Fedify to implement ActivityPub federation, demonstrating its effectiveness in real-world applications: ### Ghost: Federation for a major publishing platform [Ghost], a leading open-source publishing platform used by thousands of journalists, creators, and companies worldwide, chose Fedify for their ActivityPub implementation: * Built a separate ActivityPub service with Fedify rather than integrating directly into Ghost core * Leveraged Fedify to quickly implement federation while focusing on delivering value to their large user base * Contributes back features and bug fixes to the Fedify ecosystem > ActivityPub sits on top of a lot more standards than you might think, so it's > sometimes necessary to juggle a lot of complex standards to get the full > picture… This is the main reason I created Fedify. > > … > > We can definitely attest to the problems that Fedify is working hard to > solve, because even in just a few weeks of early prototyping we were running > into the issues described above right away. > > —From *[Alright, let's Fedify]* [Ghost]: https://ghost.org [Alright, let's Fedify]: https://activitypub.ghost.org/day-4/ ### Hollo: Single-user microblogging [Hollo] is a federated microblogging platform specifically designed for single users, created by the original developer of Fedify: * Initially faced challenges implementing ActivityPub from scratch, which inspired the creation of Fedify * Now powered by Fedify, offering features like Mastodon-compatible API, CommonMark support, and Misskey-style quotes * Serves as both a showcase and test case for Fedify's capabilities > Implementing ActivityPub from the ground up was quite painful for me… > I realized that I was already building a sloppy quasi-framework for > ActivityPub. I thought, “No way, I'm going to build a proper ActivityPub > framework,” and Fedify is the result. > > —Hong Minhee, creator of Fedify and Hollo, in *[Alright, let's Fedify]* [Hollo]: https://docs.hollo.social ### Hackers' Pub: Community-driven federation [Hackers' Pub] is an open-source community project building federation capabilities with Fedify: * Demonstrates how community-driven projects can leverage Fedify to implement ActivityPub * Showcases the accessibility of federation technology to developers beyond large organizations * Part of the growing ecosystem of developers adopting Fedify to join the fediverse [Hackers' Pub]: https://hackers.pub/ ### Encyclia: Bridging academic research to the fediverse [Encyclia] is an innovative service that makes [ORCID] (Open Researcher and Contributor ID) records available on the fediverse: * Bridges academic research into federated social networks by making researcher profiles and publications discoverable through ActivityPub * Allows users to follow researchers and see their new publications in their federated social feeds * Built using Fedify for ActivityPub implementation with tested interoperability across Mastodon and other fediverse platforms * Demonstrates Fedify's versatility in specialized domains beyond traditional social networking applications > Encyclia is built using Fedify, an ActivityPub-based federated server > framework that helps developers easily integrate their applications with > the fediverse… It simplifies the complex implementation of the ActivityPub > protocol, significantly reducing development time. > > —From the [Encyclia roadmap] [Encyclia]: https://encyclia.pub/ [ORCID]: https://orcid.org/ [Encyclia roadmap]: https://encyclia.pub/roadmap ### Typo Blue: Text-only blogging meets the fediverse [Typo Blue] is a Korean text-only blogging platform that embraces minimalist writing, allowing users to focus purely on text without images or attachments: * Originally featured email subscription for readers to follow blogs * Recently added opt-in ActivityPub integration using Fedify, enabling blogs to be discoverable across the fediverse * Allows readers on Mastodon, Misskey, and other ActivityPub-compatible platforms to follow and interact with blogs * Demonstrates how specialized platforms can maintain their unique identity while participating in the open social web [Typo Blue]: https://typo.blue/ ### SiliconBeest: Serverless federation on Cloudflare Workers [SiliconBeest] is a serverless fediverse platform built on Cloudflare Workers, demonstrating how Fedify can power ActivityPub federation on edge-native infrastructure: * Built a Mastodon API-compatible social networking server on Cloudflare Workers * Uses Fedify together with *@fedify/cfworkers* to handle federation, message delivery, signatures, WebFinger, and related protocol concerns * Combines Fedify with platform-native services like KV and Queues to support a serverless deployment model * Shows how Fedify can be adapted beyond conventional server environments for ActivityPub applications [SiliconBeest]: https://github.com/SJang1/siliconbeest ## Conclusion While building an ActivityPub server from scratch can be educational, it's a complex undertaking that can slow development and lead to interoperability or security issues. Fedify abstracts away these complexities, providing a robust, secure, and developer-friendly framework. By handling the low-level details of ActivityPub, WebFinger, authentication, and more, Fedify empowers you to: Develop faster : Focus on your application's core functionality and user experience Reduce errors : Leverage type safety and battle-tested components Ensure interoperability : Work seamlessly with the diverse fediverse ecosystem Improve security : Rely on built-in security mechanisms and best practices Stay flexible : Integrate with your technology stack of choice If you're looking to build a federated application on the ActivityPub protocol, Fedify offers a powerful and efficient path to success. --- --- url: /install.md description: How to install Fedify. --- # Installation ## Quick start The easiest way to start a new Fedify project is to use the `fedify init` command. It creates a new directory with a minimal Fedify project template. ### CLI toolchain First of all, you need to have the `fedify` command, the Fedify CLI toolchain, installed on your system. If you haven't installed it yet, please follow the following instructions: ::: code-group ```sh [npm] npm install -g @fedify/cli ``` ```sh [Homebrew (Linux/macOS)] brew install fedify ``` ```powershell [Scoop (Windows)] scoop install fedify ``` ```sh [Bun] bun install -g @fedify/cli ``` ```sh [Deno] deno install -gA --unstable-fs --unstable-kv -n fedify jsr:@fedify/cli ``` ::: If you use Deno earlier than 2.7.0, add `--unstable-temporal` to the Deno installation command above. There are other ways to install the `fedify` command. Please refer to the [*Installation* section](./cli.md#installation) in the *CLI toolchain* docs. ### Project setup After installing the `fedify` command, you can create a new Fedify project by running the following command: ```sh fedify init your-project-dir ``` The above command will start a wizard to guide you through the project setup. You can choose the JavaScript runtime, the package manager, and the web framework you want to integrate Fedify with, and so on. After the wizard finishes, you will have a new Fedify project in the *your-project-dir* directory. For more information about the `fedify init` command, please refer to the [*`fedify init`* section](./cli.md#fedify-init-initializing-a-fedify-project) in the *CLI toolchain* docs. [](https://asciinema.org/a/979416) ### Alternative: Using `@fedify/create` If you don't want to install the `fedify` CLI globally, you can use `@fedify/create` directly: ::: code-group ```sh [npm] npm init @fedify your-project-dir ``` ```sh [pnpm] pnpm create @fedify your-project-dir ``` ```sh [Yarn] yarn create @fedify your-project-dir ``` ```sh [Bun] bunx @fedify/create your-project-dir ``` ::: This works the same way as `fedify init` and will guide you through the same project setup wizard. > \[!TIP] > Already running a federated service on another JavaScript ActivityPub > library? See [*Migrating from other libraries*](./manual/migrate.md) for > guides covering `activitypub-express`, `@activity-kit/*`, hand-rolled > Express code, and `activitystrea.ms`. ## Manual installation Fedify is available on [JSR] for [Deno] and on [npm] for [Bun] and [Node.js]. [JSR]: https://jsr.io/@fedify/fedify [Deno]: https://deno.com/ [npm]: https://www.npmjs.com/package/@fedify/fedify [Bun]: https://bun.sh/ [Node.js]: https://nodejs.org/ ### Deno [Deno] is the primary runtime for Fedify. As a prerequisite, you need to have Deno 2.0.0 or later installed on your system. Then you can install Fedify via the following command: ```sh deno add jsr:@fedify/fedify ``` Fedify requires the [`Temporal`] API. On Deno 2.7.0 or later, it is stable and no extra setting is needed. On Deno versions earlier than 2.7.0, add `"temporal"` to the `"unstable"` field in *deno.json*: ```json{5} { "imports": { "@fedify/fedify": "jsr:@fedify/fedify" }, "unstable": ["temporal"] } ``` [`Temporal`]: https://tc39.es/proposal-temporal/docs/ ### Bun Fedify can also be used in Bun. You can install it via the following command: ```sh bun add @fedify/fedify ``` ### Node.js Fedify can also be used in Node.js. As a prerequisite, you need to have Node.js 22.0.0 or later installed on your system. Then you can install Fedify via the following command: ::: code-group ```sh [npm] npm add @fedify/fedify ``` ```sh [pnpm] pnpm add @fedify/fedify ``` ```sh [Yarn] yarn add @fedify/fedify ``` ::: We recommend using [ESM] with Fedify by adding `"type": "module"` to the *package.json* file. While Fedify also supports [CommonJS] for legacy compatibility, ESM is the preferred approach: ```json{2} { "type": "module", "dependencies": { "@fedify/fedify": "^1.8.1" } } ``` [ESM]: https://nodejs.org/api/esm.html [CommonJS]: https://nodejs.org/docs/latest/api/modules.html ## Editor setup `fedify init` configures Visual Studio Code for you out of the box. Setting up [Zed] takes a few extra steps because `fedify init` does not generate *.zed/* files yet. [Zed]: https://zed.dev/ ### Visual Studio Code *For Deno projects*, `fedify init` writes a *.vscode/extensions.json* that recommends the [Deno extension]: ```json [.vscode/extensions.json] { "recommendations": ["denoland.vscode-deno"] } ``` The matching *.vscode/settings.json* turns on Deno's language server and sets it as the default formatter for JavaScript and TypeScript: ```jsonc [.vscode/settings.json] { "deno.enable": true, "deno.unstable": true, "editor.detectIndentation": false, "editor.indentSize": 2, "editor.insertSpaces": true, "[typescript]": { "editor.defaultFormatter": "denoland.vscode-deno", "editor.formatOnSave": true, "editor.codeActionsOnSave": { "source.sortImports": "always" } } // The same block applies to [javascript], [javascriptreact], and // [typescriptreact]; [json] and [jsonc] use "vscode.json-language-features". } ``` Deno's [*Set up your environment*] guide covers other editors and shells. *For Node.js/Bun projects*, `fedify init` writes a *.vscode/extensions.json* that recommends the [Oxc extension]: ```json [.vscode/extensions.json] { "recommendations": ["oxc.oxc-vscode"] } ``` The matching *.vscode/settings.json* sets Oxfmt as the default formatter and enables Oxc's fix-all action on save: ```jsonc [.vscode/settings.json] { "editor.detectIndentation": false, "editor.indentSize": 2, "editor.insertSpaces": true, "oxc.fmt.configPath": ".oxfmtrc.json", "[typescript]": { "editor.defaultFormatter": "oxc.oxc-vscode", "editor.formatOnSave": true, "editor.codeActionsOnSave": { "source.fixAll.oxc": "always" } } // The same block applies to [javascript], [javascriptreact], // [typescriptreact], [json], and [jsonc]. } ``` [Deno extension]: https://marketplace.visualstudio.com/items?itemName=denoland.vscode-deno [*Set up your environment*]: https://docs.deno.com/runtime/getting_started/setup_your_environment/ [Oxc extension]: https://marketplace.visualstudio.com/items?itemName=oxc.oxc-vscode ### Zed > \[!TIP] > `fedify init` does not generate *.zed/* configuration; the snippets below > need to be added by hand. *For Deno projects*, create *.zed/settings.json* that enables the Deno language server and uses it as the formatter for TypeScript and JavaScript. The `!` prefix disables the bundled TypeScript and ESLint servers so they do not compete with Deno: ```jsonc [.zed/settings.json] { "lsp": { "deno": { "settings": { "deno": { "enable": true } } } }, "languages": { "TypeScript": { "formatter": [ { "language_server": { "name": "deno" } } ], "language_servers": [ "deno", "!typescript-language-server", "!vtsls", "!eslint", "!biome", "..." ] } // The same block applies to TSX and JavaScript. } } ``` Zed's [Deno language docs][zed-deno] cover the available options. [Hackers' Pub] is a real-world reference. *For Node.js/Bun projects*, Zed enables its bundled TypeScript language server out of the box; the typical addition is wiring up Oxc for formatting and lint fixes. See Zed's [TypeScript language docs][zed-ts] for setup. Set Oxc as the language server and turn on its fix-all action on format: ```jsonc [.zed/settings.json] { "language_servers": ["oxc", "..."], "code_actions_on_format": { "source.fixAll.oxc": true } } ``` [zed-deno]: https://zed.dev/docs/languages/deno [Hackers' Pub]: https://github.com/hackers-pub/hackerspub/blob/main/.zed/settings.json [zed-ts]: https://zed.dev/docs/languages/typescript ## Linting `fedify init` configures the [`@fedify/lint`] plugin automatically: Deno projects pick it up through `lint.plugins` in *deno.json*, and Node.js/Bun projects through a generated *.oxlintrc.json* using `@fedify/lint/oxlint`. For the full rule list, configuration options, and how to run the linter, see the [*Linting*](./manual/lint.md) chapter. [`@fedify/lint`]: https://jsr.io/@fedify/lint ## Agentic skill *This skill is available since Fedify 2.2.0.* Fedify ships a [Markdown skill file][SKILL.md] that AI coding assistants such as [Claude Code], [Codex], or [OpenCode] can load to learn Fedify's APIs, common pitfalls, and recommended patterns. The file lives inside the `@fedify/fedify` package itself, so the only remaining step is exposing it to your agent's skills directory. [SKILL.md]: https://github.com/fedify-dev/fedify/blob/main/claude-plugin/skills/fedify/SKILL.md [Claude Code]: https://claude.com/product/claude-code [Codex]: https://developers.openai.com/codex [OpenCode]: https://opencode.ai/ ### Claude Code plugin Install the Fedify plugin from the Claude Code marketplace. The plugin works with any runtime and adds six slash commands, two specialized agents, and the full Fedify skill: ``` /plugin marketplace add fedify-dev/fedify /plugin install fedify@fedify ``` ### Node.js/Bun Use [`skills-npm`], a third-party tool by Anthony Fu (it is not a Fedify package), that scans *node\_modules* for packages exposing the `agents.skills` field in their *package.json* and links them into your agent's skills directory on every install. 1. Install `skills-npm` as a dev dependency: ::: code-group ```sh [npm] npm add -D skills-npm ``` ```sh [pnpm] pnpm add -D skills-npm ``` ```sh [Yarn] yarn add -D skills-npm ``` ```sh [Bun] bun add -D skills-npm ``` ::: 2. Add a `prepare` script to your *package.json* so it runs after every install: ```json { "scripts": { "prepare": "skills-npm" } } ``` 3. Reinstall once. The Fedify skill appears at *.claude/skills/fedify/* (and similar locations for other supported agents). The same script picks up other Fedify packages and any third-party npm packages that adopt the convention. [`skills-npm`]: https://github.com/antfu/skills-npm ### Deno Install the skill by hand: 1. Pick your agent's skills directory. For Claude Code, this is *.claude/skills/fedify/*. 2. Download *SKILL.md* from the Fedify repository: ```sh mkdir -p .claude/skills/fedify curl -L -o .claude/skills/fedify/SKILL.md \ https://raw.githubusercontent.com/fedify-dev/fedify/main/claude-plugin/skills/fedify/SKILL.md ``` 3. Either commit the file or add it to *.gitignore*, depending on your team's preference. --- --- url: /cli.md description: >- The fedify command is a CLI toolchain for Fedify and debugging ActivityPub-enabled federated server apps. This section explains the key features of the fedify command. --- # `fedify`: CLI toolchain The `fedify` command is a CLI toolchain for Fedify and debugging ActivityPub-enabled federated server apps. Although it is primarily designed for developers who use Fedify, it can be used with any ActivityPub-enabled server. ## Installation ### Using npm If you have [Node.js] or [Bun] installed, you can install `fedify` by running the following command: ::: code-group ```sh [npm] npm install -g @fedify/cli ``` ```sh [Bun] bun install -g @fedify/cli ``` ::: [Node.js]: https://nodejs.org/ [Bun]: https://bun.sh/ ### Using Homebrew on macOS and Linux If you are using macOS or Linux and have [Homebrew] installed, you can install `fedify` by running the following command: ```sh brew install fedify ``` [Homebrew]: https://brew.sh/ ### Using Scoop on Windows If you are using Windows and have [Scoop] installed, you can install `fedify` by running the following command: ```powershell scoop install fedify ``` [Scoop]: https://scoop.sh/ ### Using Deno If you have [Deno] installed, you can install `fedify` by running the following command: ::: code-group ```sh [Linux] deno install \ -gA \ --unstable-fs --unstable-kv \ -n fedify \ jsr:@fedify/cli ``` ```sh [macOS] deno install \ -gA \ --unstable-fs --unstable-kv \ -n fedify \ jsr:@fedify/cli ``` ```powershell [Windows] deno install ` -gA ` --unstable-fs --unstable-kv ` -n fedify ` jsr:@fedify/cli ``` ::: On Deno versions earlier than 2.7.0, add `--unstable-temporal` to the install command above. [Deno]: https://deno.com/ ### Using mise If you have [mise] installed, you can install `fedify` by running the following command: ```sh mise use -g github:fedify-dev/fedify ``` [mise]: https://mise.jdx.dev/ ### Downloading the executable You can download the pre-built executables from the [releases] page. Download the appropriate executable for your platform and put it in your `PATH`. [releases]: https://github.com/fedify-dev/fedify/releases ## Configuration file *This feature is available since Fedify 2.0.0.* The `fedify` command supports configuration files to set default values for command options. Configuration files are written in [TOML] format. [TOML]: https://toml.io/ ### Configuration file locations Configuration files are loaded from the following locations in order, with later files taking precedence over earlier ones: 1. System-wide configuration directories (see below) 2. *~/.config/fedify/config.toml* (user-level, or `$XDG_CONFIG_HOME/fedify/config.toml`) 3. *.fedify.toml* in the current working directory (project-level) 4. Custom path specified via `--config` option (highest precedence) The system-wide and user-level config paths vary by operating system: * **Linux/macOS (system)**: Directories listed in `$XDG_CONFIG_DIRS` (default: */etc/xdg/fedify/config.toml*) * **Linux/macOS (user)**: `$XDG_CONFIG_HOME/fedify/config.toml` (default: *~/.config/fedify/config.toml*) * **Windows (system)**: *%ProgramData%\fedify\config.toml* * **Windows (user)**: *%APPDATA%\fedify\config.toml* ### `--config`: Load an additional configuration file You can specify an additional configuration file to load using the `--config` option: ```sh fedify --config ./my-config.toml lookup @user@example.com ``` This file is loaded after all standard configuration files and takes the highest precedence. ### `--ignore-config`: Ignore all configuration files The `--ignore-config` option skips loading all configuration files. This is useful for CI environments or when you want reproducible behavior: ```sh fedify --ignore-config lookup @user@example.com ``` ### Configuration file structure Below is an example configuration file showing all available options: ```toml # Global settings (apply to all commands) debug = false userAgent = "MyApp/1.0" tunnelService = "fedify.com.es" # Or "serveo.net" or "pinggy.io" [webfinger] allowPrivateAddress = false maxRedirection = 5 [lookup] authorizedFetch = false firstKnock = "draft-cavage-http-signatures-12" # or "rfc9421" allowPrivateAddress = false traverse = false suppressErrors = false reverse = false defaultFormat = "default" # "default", "raw", "compact", or "expand" separator = "----" timeout = 30 # seconds [inbox] actorName = "Fedify Ephemeral Actor" actorSummary = "An ephemeral actor for testing purposes." authorizedFetch = false noTunnel = false follow = ["@user@example.com"] acceptFollow = ["*"] [relay] protocol = "mastodon" # or "litepub" port = 8000 name = "Fedify Relay" persistent = "/path/to/relay.db" # optional, uses in-memory if not specified noTunnel = false acceptFollow = ["*"] rejectFollow = [] [nodeinfo] raw = false bestEffort = false showFavicon = true showMetadata = false ``` All configuration options are optional. Command-line arguments always take precedence over configuration file values. ## `fedify init`: Initializing a Fedify project *This command is available since Fedify 0.12.0.* [](https://asciinema.org/a/979416) The `fedify init` command is used to initialize a new Fedify project. It creates a new directory with the necessary files and directories for a Fedify project. To create a new Fedify project, run the below command: ```sh fedify init my-fedify-project ``` The above command will start the interactive prompt to initialize a new Fedify project. It will ask you a few questions to set up the project: * Web framework: Bare-bones, [Hono], [Elysia], [Express], [Nitro], [Next.js], or [Astro] * Package manager: [Deno], [Bun], [npm], [pnpm], or [Yarn] * Message queue: In-Process, [Redis], [PostgreSQL], [AMQP] (e.g., [RabbitMQ]), or [Deno KV] (if Deno) * Key–value store: In-Memory, [Redis], [PostgreSQL], or [Deno KV] (if Deno) > \[!TIP] > Projects created with `fedify init` automatically include [`@fedify/lint`] > for consistent code linting. Deno projects get a lint plugin configured in > *deno.json*, while Node.js and Bun projects get an *.oxlintrc.json* with > `@fedify/lint/oxlint`. > \[!NOTE] > If you find the full `@fedify/cli` toolchain too heavy for your needs, you > can use [`@fedify/create`] instead to scaffold a new Fedify project without > installing the CLI globally. See the > [*Alternative: Using `@fedify/create`*](./install.md#alternative-using-fedify-create) > section for details. Alternatively, you can specify the options in the command line to skip some of interactive prompts: [Hono]: https://hono.dev/ [Elysia]: https://elysiajs.com/ [Express]: https://expressjs.com/ [Nitro]: https://nitro.unjs.io/ [Next.js]: https://nextjs.org/ [Astro]: https://astro.build/ [npm]: https://www.npmjs.com/ [pnpm]: https://pnpm.io/ [Yarn]: https://yarnpkg.com/ [Redis]: https://redis.io/ [PostgreSQL]: https://www.postgresql.org/ [AMQP]: https://www.amqp.org/ [RabbitMQ]: https://www.rabbitmq.com/ [Deno KV]: https://deno.com/kv [`@fedify/lint`]: /manual/lint [`@fedify/create`]: https://www.npmjs.com/package/@fedify/create ### `-p`/`--package-manager`: Package manager You can specify the package manager by using the `-p`/`--package-manager` option. The available options are: * `deno`: [Deno] * `pnpm`: [pnpm] * `bun`: [Bun] * `yarn`: [Yarn] * `npm`: [npm] ### `-w`/`--web-framework`: Web framework You can specify the web framework to integrate with Fedify by using the `-w`/`--web-framework` option. The available options are: * `bare-bones`: A minimal setup without any web framework integration, but in Node.js, [Hono] is used for a simple adapter for a lightweight experience. * `hono`: [Hono] * `nitro`: [Nitro] * `next`: [Next.js] * `elysia`: [Elysia] * `astro`: [Astro] * `express`: [Express] ### `-k`/`--kv-store`: key–value store You can specify the key–value store to use by using the `-k`/`--kv-store` option. The available options are: * `in-memory`: An in-memory key–value store that does not persist data across restarts. This is useful for testing and development purposes. * `redis`: [Redis] * `postgres`: [PostgreSQL] * `denokv`: [Deno KV] (if Deno) ### `-m`/`--message-queue`: Message queue You can specify the message queue to use by using the `-m`/`--message-queue` option. The available options are: * `in-process`: An in-process message queue that does not persist messages across restarts. This is useful for testing and development purposes. * `redis`: [Redis] * `postgres`: [PostgreSQL] * `amqp`: [AMQP] (e.g., [RabbitMQ]) * `denokv`: [Deno KV] (if Deno) ### `--dry-run`: Preview without creating files *This option is available since Fedify 1.8.0.* The `--dry-run` option allows you to preview what files and configurations would be created without actually creating them. This is useful for reviewing the project structure before committing to the initialization. ```sh fedify init my-fedify-project --dry-run ``` When using `--dry-run`, the command will: * Display all files that would be created with their contents * Show which dependencies would be installed * Preview any commands that would be executed * Not create any directories or files on your filesystem This option works with all other initialization options, allowing you to preview different configurations before making a decision. ### `--allow-non-empty`: Initialize in a non-empty directory *This option is available since Fedify 2.2.0.* By default, `fedify init` asks for confirmation before using a directory that already contains files. This prompt protects you from accidentally initializing a project in the wrong directory. In non-interactive scripts or CI jobs, use the `--allow-non-empty` option to allow a non-empty target directory: ```sh fedify init . --allow-non-empty ``` This option does not overwrite existing project files. Before making changes, `fedify init` checks the files it would generate and fails if any of them already exist. Unrelated files can remain in the target directory only when the selected framework scaffolder accepts them. Some scaffolders, such as *create-next-app*, still reject unrelated files even if `fedify init` skips its own confirmation prompt, while a freshly initialized *.git* directory remains acceptable. ### `--skip-install`: Skip installing dependencies *This option is available since Fedify 2.3.0.* By default, `fedify init` runs the selected package manager's install command right after scaffolding the project. The `--skip-install` option scaffolds the files without running install, which is useful when: * installation is handled separately in a CI pipeline; * the new project lives inside a monorepo whose dependencies are installed from the workspace root; or * you want to inspect the generated files before installing. ```sh fedify init my-fedify-project --skip-install ``` After scaffolding, `fedify init` prints the command to run to install dependencies later. Other steps such as creating files, applying patches, and running the framework-specific scaffolder (e.g., *create-next-app*) still happen as usual; only the final install step is skipped. ## `fedify lookup`: Looking up an ActivityPub object The `fedify lookup` command is used to look up an ActivityPub object by its URL or an actor by its handle. For example, the below command looks up a `Note` object with the given URL: ```sh fedify lookup https://todon.eu/@hongminhee/112341925069749583 ``` The output will be like the below: ``` Note { id: URL "https://todon.eu/users/hongminhee/statuses/112341925069749583", attachments: [ Document { name: "The demo video on my terminal", url: URL "https://todon.eu/system/media_attachments/files/112/341/916/300/016/369/original/f83659866f94054f.mp"... 1 more character, mediaType: "video/mp4" } ], attribution: URL "https://todon.eu/users/hongminhee", contents: [ '
I'm working on adding a CLI toolchain to \[!NOTE]
> `--recurse` and [`-t`/`--traverse`](#t-traverse-traverse-the-collection)
> are mutually exclusive.
>
> Recursive fetches disallow private/localhost addresses by default for
> safety. URLs explicitly provided on the command line always allow private
> addresses, while recursive object fetches honor
> [`-p`/`--allow-private-address`](#p-allow-private-address-allow-private-ip-addresses)
> when you explicitly opt in. Recursive JSON-LD `@context` URLs still remain
> blocked.
### `--recurse-depth`: Set recursion depth limit
*This option is available since Fedify 2.1.0.*
The `--recurse-depth` option sets the maximum recursion depth when using
[`--recurse`](#recurse-recurse-through-object-relationships). By default, it
is set to `20`.
```sh
fedify lookup --recurse=replyTarget --recurse-depth=10 https://hollo.social/@fedify/019c8522-b247-79d3-b0e7-c6a2293bb1cf
```
This option depends on the `--recurse` option.
### `-S`/`--suppress-errors`: Suppress partial errors during traversal or recursion
*This option is available since Fedify 0.14.0.*
The `-S`/`--suppress-errors` option is used to suppress partial errors during
traversal or recursion.
For traversal mode:
```sh
fedify lookup --traverse --suppress-errors https://fosstodon.org/users/hongminhee/outbox
```
For recursion mode:
```sh
fedify lookup --recurse=replyTarget --suppress-errors https://hollo.social/@fedify/019c8522-b247-79d3-b0e7-c6a2293bb1cf
```
The difference between with and without the `-S`/`--suppress-errors` option is
that the former will suppress the partial errors during traversal or recursion,
while the latter will stop on the first such error.
### `-c`/`--compact`: Compact JSON-LD
> \[!NOTE]
> This option is mutually exclusive with `-e`/`--expanded` and `-r`/`--raw`.
You can also output the object in the [compacted JSON-LD] format by using the
`-c`/`--compact` option:
```sh
fedify lookup --compact https://todon.eu/@hongminhee/112341925069749583
```
The output will be like the below:
```json
{
"@context": "https://www.w3.org/ns/activitystreams",
"id": "https://todon.eu/users/hongminhee/statuses/112341925069749583",
"type": "Note",
"attachment": {
"type": "Document",
"mediaType": "video/mp4",
"name": "The demo video on my terminal",
"url": "https://todon.eu/system/media_attachments/files/112/341/916/300/016/369/original/f83659866f94054f.mp4"
},
"attributedTo": "https://todon.eu/users/hongminhee",
"cc": "https://todon.eu/users/hongminhee/followers",
"content": " I'm working on adding a CLI toolchain to #Fedify to help with debugging. The first feature I implemented is the ActivityPub object lookup. Here's a demo. I'm working on adding a CLI toolchain to #Fedify to help with debugging. The first feature I implemented is the ActivityPub object lookup. Here's a demo. I'm working on adding a CLI toolchain to #Fedify to help with debugging. The first feature I implemented is the ActivityPub object lookup. Here's a demo. I'm working on adding a CLI toolchain to #Fedify to help with debugging. The first feature I implemented is the ActivityPub object lookup. Here's a demo. I'm working on adding a CLI toolchain to #Fedify to help with debugging. The first feature I implemented is the ActivityPub object lookup. Here's a demo. I'm working on adding a CLI toolchain to #Fedify to help with debugging. The first feature I implemented is the ActivityPub object lookup. Here's a demo. Hello, JSX! Hello, {getName()}! Hello, {getName()}! Hello, JSX! {handle}
{handle} ·{" "}
{followers === 1 ? "1 follower" : `${followers} followers`}
{handle} ·{" "}
{following} following{" "}
·{" "}
{followers === 1 ? "1 follower" : `${followers} followers`}
Would it send a Create(Note) activity? A tiny federated image sharing service.
PxShare is a single-user federated service. Choose the one account this
instance will host.
@{{ user.username }} A tiny federated image sharing service.
{{ followers.length }}
{{ followers.length === 1 ? "follower" : "followers" }}
No followers yet.
@{{ user.username }}
Pick an image, add a caption, and share it with your followers.
${followers.map((f) => `
`,
{
headers: { "Content-Type": "text/html; charset=utf-8" },
},
);
}
// The federation-related requests are handled by the Federation object:
return await federation.fetch(request, { contextData: undefined });
});
```
```typescript{4-18} twoslash [Bun]
import "bun";
import type { Federation } from "@fedify/fedify";
import { openKv } from "@deno/kv";
const federation = null as unknown as Federation${followers.map((f) => `
`,
{
headers: { "Content-Type": "text/html; charset=utf-8" },
},
);
}
// The federation-related requests are handled by the Federation object:
return await federation.fetch(request, { contextData: undefined });
}
});
```
```typescript{4-18} twoslash [Node.js]
import { serve } from "@hono/node-server";
import type { Federation } from "@fedify/fedify";
import { openKv } from "@deno/kv";
const federation = null as unknown as Federation${followers.map((f) => `
`,
{
headers: { "Content-Type": "text/html; charset=utf-8" },
},
);
}
// The federation-related requests are handled by the Federation object:
return await federation.fetch(request, { contextData: undefined });
}
});
```
:::
The above code lists the actor's followers on the home page. The followers are
stored in the key–value store, and we retrieve the followers from the key–value
store and display them on the home page.
Restart the server and navigate to the home page in your web browser. You
should see the actor's followers in the bulleted list.
> \[!NOTE]
> Although the above code handles the home page *before* the federation-related
> requests, it is just an example. In a real-world application, you should
> integrate Fedify with a web framework like [Hono], where the order of
> handling requests is reversed: the federation-related requests are handled
> *before* other web-related requests.
>
> For more details, see the [*Integration* section](../manual/integration.md).
[Hono]: https://hono.dev/
## Linting your federation code
As your federated server grows, it's important to maintain code quality and
catch common mistakes early. Fedify provides the [`@fedify/lint`] package
with linting plugins for both Deno Lint and ESLint, which include specialized
linting rules for federation code.
The `@fedify/lint` package helps you avoid common pitfalls such as:
* Using incorrect actor IDs (e.g., relative URLs instead of
`Context.getActorUri()`)
* Missing required actor properties (inbox, outbox, followers, etc.)
* Incorrect URL patterns for actor collections
* Missing public keys or assertion methods
For example, if you had written the actor dispatcher like this:
```typescript
// ❌ Wrong: Using relative URL
federation.setActorDispatcher(
"/{identifier}",
(_ctx, identifier) => {
return new Person({
id: new URL(`/${identifier}`), // ❌ Linter will catch this
name: "Example User",
});
},
);
```
The linter would warn you:
```
error[fedify-lint/actor-id-mismatch]: Actor's `id` property must match
`_ctx.getActorUri(identifier)`. Ensure you're using the correct context method.
```
This helps you catch mistakes before they cause issues in production.
> \[!TIP]
> For detailed setup instructions, available rules, and configuration options,
> see the [*Linting* section](../manual/lint.md) in the manual.
[`@fedify/lint`]: https://jsr.io/@fedify/lint
## Wrapping up
Congratulations! You have built a small federated server that can accept
follow requests and list the actor's followers. You have learned the key
features of the ActivityPub protocol and the Fedify framework, such as actors,
sending and receiving activities, and the inbox.
In this tutorial, we have covered the following topics:
* Creating a new project
* Creating the server
* `Federation` object
* Actor dispatcher
* Exposing the server to the public internet
* Inbox listener
* Generating a key pair
* Sending an `Accept` activity
* Listing followers
You can extend the server by adding more features, such as sending other
activities, handling other types of activities, and implementing other callback
functions. The Fedify framework provides a wide range of features to build
a federated server, and you can explore them by reading
the [manual](../manual/federation.md) and the [API reference].
If you have any questions or feedback, feel free to ask in
the [Fedify community] on Matrix or the [GitHub Discussions].
[API reference]: https://jsr.io/@fedify/fedify
[Fedify community]: https://matrix.to/#/#fedify:matrix.org
[GitHub Discussions]: https://github.com/fedify-dev/fedify/discussions
## Exercises
* Implement unfollowing feature: Listen to the `Undo` activity and remove
the follower from the key–value store when the server receives an `Undo`
activity.
* Integration with a web framework: In the above example, we hard-coded
the home page inside the callback function passed to `Deno.serve()`
(in case of Deno), `Bun.serve()` (in case of Bun), and `serve()` (in case
of Node.js). This is not enough for a real-world application as you would
need to rendering HTML templates, handling media files, and so on.
Instead, you can use a web framework like [Hono] or [Fresh] to utilize
the proper routing system and [JSX] templates to produce HTML.
See also the [*Integration* section](../manual/integration.md) in
the manual for more details.
[Fresh]: https://fresh.deno.dev/
[JSX]: https://facebook.github.io/jsx/
---
---
url: /tutorial/microblog.md
description: >-
In this tutorial, we will build a small microblog that implements the
ActivityPub protocol, similar to Mastodon or Misskey, using Fedify, an
ActivityPub server framework.
---
# Creating your own federated microblog
> \[!TIP]
>
> This tutorial is also available in the following languages: [한국어] (Korean)
> and [日本語] (Japanese).
In this tutorial, we will build a small [microblog] that implements
the ActivityPub protocol, similar to [Mastodon] or [Misskey], using [Fedify],
an ActivityPub server framework. This tutorial will focus more on how to
use Fedify rather than understanding its underlying operating principles.
If you have any questions, suggestions, or feedback, please feel free to join
our [Matrix chat space] or [GitHub Discussions].
[한국어]: https://hackers.pub/@hongminhee/2025/fedify-tutorial-ko
[日本語]: https://zenn.dev/hongminhee/books/4a38b6358a027b
[microblog]: https://en.wikipedia.org/wiki/Microblogging
[Mastodon]: https://joinmastodon.org/
[Misskey]: https://misskey-hub.net/
[Fedify]: https://fedify.dev/
[Matrix chat space]: https://matrix.to/#/#fedify:matrix.org
[GitHub Discussions]: https://github.com/fedify-dev/fedify/discussions
## Target audience
This tutorial is aimed at those who want to learn Fedify and create ActivityPub
server software.
We assume that you have experience in creating web applications using HTML
and HTTP, and that you understand command-line interfaces, SQL, JSON,
and basic JavaScript. However, you don't need to know TypeScript, JSX,
ActivityPub, or Fedify—we'll teach you what you need to know about these as
we go along.
You don't need experience in creating ActivityPub software, but we do assume
that you've used at least one ActivityPub software like Mastodon or Misskey.
This is so you have an idea of what we're trying to build.
\*\[JSX]: JavaScript XML
## Goals
In this tutorial, we'll use Fedify to create a single-user microblog that can
communicate with other federated software and services via ActivityPub.
This software will include the following features:
* Only one account can be created.
* Other accounts in the fediverse can follow the user.
* Followers can unfollow the user.
* The user can view their list of followers.
* The user can create posts.
* The user's posts are visible to followers in the fediverse.
* The user can follow other accounts in the fediverse.
* The user can view a list of accounts they are following.
* The user can view a chronological list of posts from accounts they follow.
To simplify the tutorial, we'll impose the following feature constraints:
* Account profiles (bio, photos, etc.) cannot be set.
* Once created, an account cannot be deleted.
* Once posted, a post cannot be edited or deleted.
* Once followed, another account cannot be unfollowed.
* There are no likes, shares, or comments.
* There is no search functionality.
* There are no security features such as authentication or permission checks.
Of course, after completing the tutorial, you're welcome to add these
features—it would be good practice!
The complete source code is available in the [GitHub repository],
with commits separated according to each implementation step for your reference.
[GitHub repository]: https://github.com/fedify-dev/microblog
## Setting up the development environment
### Installing Node.js
Fedify supports three JavaScript runtimes: [Deno], [Bun], and [Node.js].
Among these, Node.js is the most widely used, so we'll use Node.js as the basis
for this tutorial.
> \[!TIP]
> A JavaScript runtime is a platform that executes JavaScript code. Web browsers
> are one type of JavaScript runtime, and for command-line or server use,
> Node.js is widely used. Recently, cloud edge functions like
> [Cloudflare Workers] have also gained popularity as JavaScript runtimes.
To use Fedify, you need Node.js version 22.0.0 or higher. There are
[various installation methods]—choose the one that suits you best.
Once Node.js is installed, you'll have access to the `node` and `npm` commands:
```sh
node --version
npm --version
```
[Deno]: https://deno.com/
[Bun]: https://bun.sh/
[Node.js]: https://nodejs.org/
[Cloudflare Workers]: https://workers.cloudflare.com/
[various installation methods]: https://nodejs.org/en/download/package-manager
### Installing the `fedify` command
To set up a Fedify project, you need to install the [`fedify`](../cli.md)
command on your system. There are
[several installation methods](../cli.md#installation),
but using the `npm` command is the simplest:
```sh
npm install -g @fedify/cli
```
After installation, check if you can use the `fedify` command. You can check
the version of the `fedify` command with this command:
```sh
fedify --version
```
Make sure the version number is 1.0.0 or higher. If it's an older version,
you won't be able to properly follow this tutorial.
### `fedify init` to initialize the project
To start a new Fedify project, let's decide on a directory path to work in.
In this tutorial, we'll name it *microblog*. Run the
[`fedify init`](../cli.md#fedify-init-initializing-a-fedify-project) command
followed by the directory path (it's okay if the directory doesn't exist yet):
```sh
fedify init microblog
```
When you run the `fedify init` command, you'll see a series of prompts.
Select *Hono*, *npm*, *in-process*, and *in-memory* in order:
```console
___ _____ _ _ __
/'_') | ___|__ __| (_)/ _|_ _
.-^^^-/ / | |_ / _ \/ _` | | |_| | | |
__/ / | _| __/ (_| | | _| |_| |
<__.|_|-|_| |_| \___|\__,_|_|_| \__, |
|___/
? Choose the web framework to use
Bare-bones
❯ Hono
Nitro
Next.js
ElysiaJS
Astro
Express
Nuxt
SolidStart
? Choose the package manager to use
deno
pnpm
bun
yarn
❯ npm
? Choose the message queue to use
❯ in-process
redis
postgres
mysql
amqp
- denokv (not supported with npm) (disabled)
? Choose the key-value store to use
❯ in-memory
redis
postgres
mysql
- denokv (not supported with npm) (disabled)
```
> \[!NOTE]
> Fedify is not a full-stack framework, but rather a framework specialized for
> implementing ActivityPub servers. Therefore, it's designed to be used
> alongside other web frameworks. In this tutorial, we'll adopt [Hono] as
> our web framework to use with Fedify.
After a moment, you'll see files created in your working directory with
the following structure:
* *.vscode/* — Visual Studio Code related settings
* *extensions.json* — Recommended extensions for Visual Studio Code
* *settings.json* — Visual Studio Code settings
* *node\_modules/* — Directory where dependent packages are installed
(contents omitted)
* *src/* — Source code
* *app.tsx* — Server unrelated to ActivityPub
* *federation.ts* — ActivityPub server
* *index.ts* — Entry point
* *logging.ts* — Logging configuration
* *.oxfmtrc.json* — Formatter settings
* *.oxlintrc.json* — Linter settings
* *package.json* — Package metadata
* *tsconfig.json* — TypeScript settings
As you might guess, we're using TypeScript instead of JavaScript,
which is why we have *.ts* and *.tsx* files instead of *.js* files.
The generated source code is a working demo. Let's first check if it runs
properly:
```sh
npm run dev
```
This command will keep the server running until you press
Ctrl+C:
```console
Server started at http://0.0.0.0:8000
```
With the server running, open a new terminal tab and run the following command:
```sh
fedify lookup http://localhost:8000/users/john
```
This command queries an actor (actor) on the ActivityPub server we've set up
locally. In ActivityPub, an actor can be thought of as an account that's
accessible across various ActivityPub servers.
If you see output like this, it's working correctly:
```console
✔ Looking up the object...
Person {
id: URL "http://localhost:8000/users/john",
name: "john",
preferredUsername: "john"
}
```
This result tells us that there's an actor object located at the */users/john*
path, it's of type `Person`, its ID is *http://localhost:8000/users/john*,
its name is *john*, and its username is also *john*.
> \[!TIP]
> [`fedify lookup`](../cli.md#fedify-lookup-looking-up-an-activitypub-object)
> is a command to query ActivityPub objects. This is equivalent
> to searching with the corresponding URI on Mastodon. (Of course, since your
> server is only accessible locally at the moment, searching on Mastodon won't
> yield any results yet.)
>
> If you prefer `curl` over the `fedify lookup` command, you can also query
> the actor with this command (note that we're sending the Accept
> header with the `-H` option):
>
> ```sh
> curl -H"Accept: application/activity+json" http://localhost:8000/users/john
> ```
>
> However, if you query like this, the result will be in JSON format,
> which is difficult to read with the naked eye. If you also have the `jq`
> command installed on your system, you can use `curl` and `jq` together:
>
> ```sh
> curl -H"Accept: application/activity+json" http://localhost:8000/users/john | jq .
> ```
[Hono]: https://hono.dev/
### Visual Studio Code
[Visual Studio Code] might not be your favorite editor. However, we recommend
using Visual Studio Code while following this tutorial. This is because we need
to use TypeScript, and Visual Studio Code is currently the most convenient and
excellent TypeScript IDE. Also, the generated project setup already includes
Visual Studio Code settings, so you don't have to wrestle with formatters or
linters.
> \[!WARNING]
> Don't confuse this with Visual Studio. Visual Studio Code and Visual Studio
> only share a brand name; they are completely different software.
After [installing Visual Studio Code], open the working directory by selecting
*File* → *Open Folder…* from the menu.
If you see a popup in the bottom right asking you to install the recommended
Oxc extension for this repository, click the *Install* button to install the
extension. Installing this extension will automatically format your TypeScript
code, so you don't have to wrestle with code styles like indentation or
spacing when writing TypeScript code.
> \[!TIP]
> If you're a loyal Emacs or Vim user, we won't discourage you from using your
> favorite editor. However, we recommend setting up TypeScript LSP.
> The difference in productivity depending on whether TypeScript LSP is set up
> or not is significant.
\*\[LSP]: Language Server Protocol
[Visual Studio Code]: https://code.visualstudio.com/
[installing Visual Studio Code]: https://code.visualstudio.com/docs/setup/setup-overview
## Prerequisites
### TypeScript
Before we start modifying code, let's briefly go over TypeScript.
If you're already familiar with TypeScript, you can skip this section.
TypeScript adds static type checking to JavaScript. The TypeScript syntax is
almost the same as JavaScript, but the main difference is that you can specify
types for variables and functions. Types are specified by adding a colon (`:`)
after the variable or parameter.
For example, the following code indicates that the `foo` variable is a `string`:
```typescript twoslash
let foo: string;
```
If you try to assign a value of a different type to a variable declared like
this, Visual Studio Code will show a red underline *before you even run it* and
display a type error:
```typescript twoslash
// @errors: 2322
let foo: string;
// ---cut-before---
foo = 123;
```
When coding, don't ignore red underlines. If you ignore them and run
the program, it's likely that an error will actually occur at that part.
The most common type of error you'll encounter when coding in TypeScript is
the `null` possibility error. For example, in the following code, the `bar`
variable can be either a `string` or `null` (`string | null`):
```typescript twoslash
function someFunction(): string | null { return ""; }
// ---cut-before---
const bar: string | null = someFunction();
```
What happens if you try to get the first character of this variable's content
like this?
```typescript twoslash
// @errors: 18047
function someFunction(): string | null { return ""; }
const bar: string | null = someFunction();
// ---cut-before---
const firstChar = bar.charAt(0);
```
You'll get a type error like above. It's saying that `bar` might sometimes be
`null`, and in that case, calling `null.charAt(0)` would cause an error,
so you need to fix the code. In such cases, you need to add handling for
the `null` case like this:
```typescript twoslash
function someFunction(): string | null { return ""; }
const bar: string | null = someFunction();
// ---cut-before---
const firstChar = bar === null ? "" : bar.charAt(0);
```
In this way, TypeScript helps prevent bugs by making you think of cases you
might not have considered when coding.
Another incidental advantage of TypeScript is that it enables auto-completion.
For example, if you type `foo.`, a list of methods available for string objects
will appear, allowing you to choose from them. This allows for faster coding
without having to check documentation each time.
We hope you'll feel the charm of TypeScript as you follow this tutorial.
Above all, Fedify provides the best experience when used with TypeScript.
> \[!TIP]
> If you want to learn TypeScript properly and thoroughly, we recommend reading
> *[The TypeScript Handbook]*. It takes about 30 minutes to read it all.
[The TypeScript Handbook]: https://www.typescriptlang.org/docs/handbook/intro.html
### JSX
JSX is a syntax extension of JavaScript that allows you to insert XML or HTML
into JavaScript code. It can also be used in TypeScript, in which case it's
sometimes called TSX. In this tutorial, we'll write all HTML within JavaScript
code using JSX syntax. Those who are already familiar with JSX can skip this
section.
For example, the following code assigns an HTML tree with a `Set up your microblog
>
);
```
In JSX, you can only have one top-level element, but the `` and `
;
export const SetupForm: FC = () => <>>;
// ---cut-before---
import { Layout, SetupForm } from "./views.tsx";
```
Then, display the account creation form we just made on the */setup* page:
```tsx twoslash [src/app.tsx]
import { Hono } from "hono";
const app = new Hono();
import type { FC } from "hono/jsx";
export const Layout: FC = (props) => {name}
Set up your microblog
>
);
```
Now `import` the Actor type we defined earlier in *src/app.tsx*:
```typescript [src/app.tsx]
import type { Actor, User } from "./schema.ts";
```
Now let's add code to the `POST /setup` handler to create a record in
the `actors` table with the input name and other necessary information:
```typescript{7,19-24,26,30-44} twoslash [src/app.tsx]
import { Hono } from "hono";
const app = new Hono();
import Database from "better-sqlite3";
const db = new Database("");
interface User {}
import type { Federation } from "@fedify/fedify";
const fedi = null as unknown as FederationFollowers
{followers.map((follower) => (
>
);
export interface ActorLinkProps {
actor: Actor;
}
export const ActorLink: FC
{name}
{user.name}'s microblog
>
);
```
Then open the *src/app.tsx* file and `import` the `Following
{following.map((actor) => (
>
);
```
Then, open the *src/app.tsx* file and `import` the `
{name}
Welcome to PxShare
Set up your PxShare instance
{{ user.name }}
Welcome to PxShare
Followers
{{ user.name }}
New post