Loading docs/.vitepress/config.mts +5 −4 Original line number Diff line number Diff line Loading @@ -88,7 +88,7 @@ export default defineConfig({ link: "https://dash.deno.com/playground/fedify-demo", }, { text: "Installation", link: "/install.md" }, { text: "Tutorial", link: "/tutorial.md" }, { text: "In-depth tutorial", link: "/tutorial.md" }, { text: "CLI toolchain", link: "/cli.md", Loading Loading @@ -127,7 +127,8 @@ export default defineConfig({ }, { icon: { svg: '<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>ActivityPub</title><path d="M10.91 4.442L0 10.74v2.52L8.727 8.22v10.077l2.182 1.26zM6.545 12l-4.364 2.52 4.364 2.518zm6.545-2.52L17.455 12l-4.364 2.52zm0-5.038L24 10.74v2.52l-10.91 6.298v-2.52L21.819 12 13.091 6.96z"/></svg>', svg: '<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>ActivityPub</title><path d="M10.91 4.442L0 10.74v2.52L8.727 8.22v10.077l2.182 1.26zM6.545 12l-4.364 2.52 4.364 2.518zm6.545-2.52L17.455 12l-4.364 2.52zm0-5.038L24 10.74v2.52l-10.91 6.298v-2.52L21.819 12 13.091 6.96z"/></svg>', }, link: "https://hollo.social/@fedify", ariaLabel: "Hollo (ActivityPub)", Loading Loading @@ -181,8 +182,8 @@ export default defineConfig({ "meta", { name: "fediverse:creator", content: "@fedify@hollo.social" } content: "@fedify@hollo.social", }, ], ...plausibleScript, ], Loading docs/install.md +68 −22 Original line number Diff line number Diff line Loading @@ -4,27 +4,77 @@ prev: text: What is Fedify? link: ./intro.md next: text: Tutorial text: In-depth tutorial link: ./tutorial.md --- Installation ============ Fedify is available on [JSR] for [Deno] and on [npm] for [Node.js] and [Bun]. > [!TIP] > We recommend using Deno or Bun (which are TypeScript-first) for the best > experience, but you can use Node.js if you prefer. 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 [Node.js] npm install -g @fedify/cli ~~~~ ~~~~ sh [Bun] bun install -g @fedify/cli ~~~~ ~~~~ sh [Deno] deno install -A --unstable-fs --unstable-kv --unstable-temporal -n fedify jsr:@fedify/cli ~~~~ ::: 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. 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 [Node.js]: https://nodejs.org/ [Bun]: https://bun.sh/ [Node.js]: https://nodejs.org/ Deno ---- ### Deno [Deno] is the primary runtime for Fedify. As a prerequisite, you need to have Deno 1.41.0 or later installed on your system. Then you can install Fedify Loading @@ -35,7 +85,7 @@ deno add @fedify/fedify ~~~~ Since Fedify requires [`Temporal`] API, which is an unstable feature in Deno as of May 2024, you need to add the `"temporal"` to the `"unstable"` field of of July 2024, you need to add the `"temporal"` to the `"unstable"` field of the *deno.json* file: ~~~~ json{5} Loading @@ -49,9 +99,16 @@ the *deno.json* file: [`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 ------- ### Node.js Fedify can also be used in Node.js. As a prerequisite, you need to have Node.js 20.0.0 or later installed on your system. Then you can install Fedify via Loading @@ -72,14 +129,3 @@ Fedify is an ESM-only package, so you need to add `"type": "module"` to the } } ~~~~ Bun --- Fedify can also be used in Bun. You can install it via the following command: ~~~~ sh bun add @fedify/fedify ~~~~ docs/manual/federation.md +77 −0 Original line number Diff line number Diff line Loading @@ -214,6 +214,83 @@ same path. Turned off by default. The `~Federation.fetch()` API ----------------------------- *This API is available since Fedify 0.6.0.* The `Federation` object provides the `~Federation.fetch()` method to handle incoming HTTP requests. The `~Federation.fetch()` method takes an incoming [`Request`] and returns a [`Response`]. Actually, this interface is de facto standard in the server-side JavaScript world, and it is inspired by the [`window.fetch()`] method in the browser environment. Therefore, you can pass it to the [`Deno.serve()`] function in [Deno], and the [`Bun.serve()`] function in [Bun]: ::: code-group ~~~~ typescript [Deno] Deno.serve( (request) => federation.fetch(request, { contextData: undefined }) ); ~~~~ ~~~~ typescript [Bun] Bun.serve({ fetch: (request) => federation.fetch(request, { contextData: undefined }), }) ~~~~ ::: However, in case of [Node.js], it has no built-in server API that takes `fetch()` callback function like Deno or Bun. Instead, you need to use [@hono/node-server] package to adapt the `~Federation.fetch()` method to the Node.js' HTTP server API: ::: code-group ~~~~ sh [Node.js] npm add @hono/node-server ~~~~ ::: And then, you can use the [`serve()`] function from the package: ::: code-group ~~~~ typescript [Node.js] import { serve } from "@hono/node-server"; serve({ fetch: (request) => federation.fetch(request, { contextData: undefined }), }) ~~~~ ::: > [!NOTE] > > Although a `Federation` object can be directly passed to the HTTP server > APIs, you would usually integrate it with a web framework. For details, > see the [*Integration* section](./integration.md). [`Request`]: https://developer.mozilla.org/en-US/docs/Web/API/Request [`Response`]: https://developer.mozilla.org/en-US/docs/Web/API/Response [`window.fetch()`]: https://developer.mozilla.org/en-US/docs/Web/API/Window/fetch [`Deno.serve()`]: https://docs.deno.com/api/deno/~/Deno.serve [Deno]: http://deno.com/ [`Bun.serve()`]: https://bun.sh/docs/api/http#bun-serve [Bun]: https://bun.sh/ [Node.js]: https://nodejs.org/ [@hono/node-server]: https://github.com/honojs/node-server [`serve()`]: https://github.com/honojs/node-server?tab=readme-ov-file#usage How the `Federation` object recognizes the domain name ------------------------------------------------------ Loading docs/tutorial.md +110 −78 Original line number Diff line number Diff line Loading @@ -8,14 +8,20 @@ prev: link: ./install.md --- Tutorial ======== In-depth tutorial ================= In this tutorial, we will build a small federated server that can only accept follow requests. Despite its simplicity, it will cover the key features of the follow requests to understand the basic concepts of the Fedify framework. Despite its simplicity, it will cover the key features of the ActivityPub protocol and the Fedify framework, such as actors, sending and receiving activities, and the inbox. This tutorial will not use the quick start project template created by the [`fedify init`](./cli.md#fedify-init-initializing-a-fedify-project) command. Instead, we will start from scratch to understand how the Fedify framework works without any boilerplate code. As prerequisite knowledge, you should have a basic understanding of JavaScript, command-line interfaces, and minimum experience with building web server apps. However, it's perfectly fine if you're not familiar with Loading Loading @@ -50,18 +56,19 @@ echo '{ "unstable": ["kv", "temporal"] }' > deno.json deno add @fedify/fedify ~~~~ ~~~~ sh [Node.js] ~~~~ sh [Bun] mkdir follow-server cd follow-server/ echo '{ "type": "module" }' > package.json npm add -D typescript tsx @types/node npm add @deno/kv @fedify/fedify @hono/node-server bun add @deno/kv @fedify/fedify ~~~~ ~~~~ sh [Bun] ~~~~ sh [Node.js] mkdir follow-server cd follow-server/ bun add @deno/kv @fedify/fedify echo '{ "type": "module" }' > package.json npm add -D typescript tsx @types/node npm add @deno/kv @fedify/fedify @hono/node-server ~~~~ ::: Loading @@ -81,6 +88,16 @@ The above commands will create a *deno.json* (in case of Deno) or *package.json* } ~~~~ ~~~ json [Bun] { "type": "module", "dependencies": { "@deno/kv": "^0.8.0", "@fedify/fedify": "^0.12.0" } } ~~~ ~~~ json [Node.js] { "type": "module", Loading @@ -96,23 +113,34 @@ The above commands will create a *deno.json* (in case of Deno) or *package.json* } ~~~ ~~~ json [Bun] { "dependencies": { "@deno/kv": "^0.8.0", "@fedify/fedify": "^0.12.0" } } ~~~ ::: > [!NOTE] > The [`"unstable"`] field in the *deno.json* file is required because Fedify > uses [`Temporal`] API, which is an unstable feature in Deno as of > July 2024. By adding `"temporal"` to the `"unstable"` field, you can use the > Fedify framework without any issues. > [!NOTE] > In Bun and Node.js, you need to add [`"type": "module"`] to the *package.json* > file because Fedify is an ESM-only package. > [!TIP] > Do you wonder why we need to add *[tsx]* and *@types/node* in the case of > Node.js? It's because Fedify is written in TypeScript, and > Node.js doesn't support TypeScript out of the box. By adding *tsx* and > *@types/node*, you can write TypeScript code in Node.js without any hassle. [^2]: The actual version number may vary depending on the latest version of the Fedify framework as of reading this tutorial. [Deno]: https://deno.com/ [Bun]: https://bun.sh/ [Node.js]: https://nodejs.org/ [`"unstable"`]: https://docs.deno.com/runtime/manual/tools/unstable_flags/#configuring-flags-in-deno.json [`Temporal`]: https://tc39.es/proposal-temporal/docs/ [`"type": "module"`]: https://nodejs.org/api/packages.html#type [tsx]: https://tsx.is/ Creating the server Loading @@ -131,10 +159,8 @@ Deno.serve(request => ); ~~~~ ~~~~ typescript [Node.js] import { serve } from "@hono/node-server"; serve({ ~~~~ typescript [Bun] Bun.serve({ port: 8000, fetch(request) { return new Response("Hello, world", { Loading @@ -144,8 +170,10 @@ serve({ }); ~~~~ ~~~~ typescript [Bun] Bun.serve({ ~~~~ typescript [Node.js] import { serve } from "@hono/node-server"; serve({ port: 8000, fetch(request) { return new Response("Hello, world", { Loading @@ -166,30 +194,30 @@ request. You can run the server by executing the following command: deno run -A server.ts ~~~~ ~~~~ sh [Node.js] node --import tsx server.ts ~~~~ ~~~~ sh [Bun] bun server.ts ~~~~ ~~~~ sh [Node.js] node --import tsx server.ts ~~~~ :::: Now, open your web browser and navigate to <http://localhost:8000/>. You should see the <q>Hello, world</q> message. As you can guess, [`Deno.serve()`] (in case of Deno), [`serve()`] (in case of Node.js), and [`Bun.serve()`] (in case of Bun) are a function to create an HTTP As you can guess, [`Deno.serve()`] (in case of Deno), [`Bun.serve()`] (in case of Bun), and [`serve()`] (in case of Node.js) are a function to create an HTTP server. They take a callback function that receives a [`Request`] object and returns a [`Response`] object. The `Response` object is sent back to the client. This server is not federated yet, but it's a good starting point to build a federated server. [`Deno.serve()`]: https://deno.land/api?s=Deno.serve [`serve()`]: https://github.com/honojs/node-server?tab=readme-ov-file#usage [`Deno.serve()`]: https://docs.deno.com/api/deno/~/Deno.serve [`Bun.serve()`]: https://bun.sh/docs/api/http#bun-serve [`serve()`]: https://github.com/honojs/node-server?tab=readme-ov-file#usage [`Request`]: https://developer.mozilla.org/en-US/docs/Web/API/Request [`Response`]: https://developer.mozilla.org/en-US/docs/Web/API/Response Loading Loading @@ -232,10 +260,8 @@ Deno.serve( ); ~~~~ ~~~~ typescript{6} [Node.js] import { serve } from "@hono/node-server"; serve({ ~~~~ typescript{4} [Bun] Bun.serve({ port: 8000, fetch(request) { return federation.fetch(request, { contextData: undefined }); Loading @@ -243,8 +269,10 @@ serve({ }); ~~~~ ~~~~ typescript{4} [Bun] Bun.serve({ ~~~~ typescript{6} [Node.js] import { serve } from "@hono/node-server"; serve({ port: 8000, fetch(request) { return federation.fetch(request, { contextData: undefined }); Loading Loading @@ -283,14 +311,14 @@ to the next step. > deno add @logtape/logtape > ~~~~ > > ~~~~ sh [Node.js] > npm add @logtape/logtape > ~~~~ > > ~~~~ sh [Bun] > bun add @logtape/logtape > ~~~~ > > ~~~~ sh [Node.js] > npm add @logtape/logtape > ~~~~ > > ::: > > Then, you can set up loggers by calling [`configure()`] function at the Loading Loading @@ -352,9 +380,8 @@ Deno.serve( ); ~~~~ ~~~~ typescript{8-17} [Node.js] ~~~~ typescript{7-16} [Bun] import { Federation, MemoryKvStore, Person } from "@fedify/fedify"; import { serve } from "@hono/node-server"; const federation = createFederation<void>({ kv: new MemoryKvStore(), Loading @@ -371,7 +398,7 @@ federation.setActorDispatcher("/users/{handle}", async (ctx, handle) => { }); }); serve({ Bun.serve({ port: 8000, fetch(request) { return federation.fetch(request, { contextData: undefined }); Loading @@ -379,8 +406,9 @@ serve({ }); ~~~~ ~~~~ typescript{7-16} [Bun] ~~~~ typescript{8-17} [Node.js] import { Federation, MemoryKvStore, Person } from "@fedify/fedify"; import { serve } from "@hono/node-server"; const federation = createFederation<void>({ kv: new MemoryKvStore(), Loading @@ -397,7 +425,7 @@ federation.setActorDispatcher("/users/{handle}", async (ctx, handle) => { }); }); Bun.serve({ serve({ port: 8000, fetch(request) { return federation.fetch(request, { contextData: undefined }); Loading @@ -423,12 +451,12 @@ WebFinger for the actor. Run the server by executing the following command: deno run -A server.ts ~~~~ ~~~~ sh [Node.js] node --import tsx server.ts ~~~~ sh [Bun] bun run server.ts ~~~~ ~~~~ sh [Bun] bun server.ts ~~~~ sh [Node.js] node --import tsx server.ts ~~~~ ::: Loading Loading @@ -557,14 +585,14 @@ To do this, you need to install the package: deno add @hongminhee/x-forwarded-fetch ~~~~ ~~~~ sh [Node.js] npm install x-forwarded-fetch ~~~~ ~~~~ sh [Bun] bun add x-forwarded-fetch ~~~~ ~~~~ sh [Node.js] npm add x-forwarded-fetch ~~~~ ::: Then, import the package and place the `behindProxy()` middleware in front of Loading @@ -580,22 +608,22 @@ Deno.serve( ); ~~~~ ~~~~ typescript{2,6} [Node.js] import { serve } from "@hono/node-server"; ~~~~ typescript{1,5} [Bun] import { behindProxy } from "x-forwarded-fetch"; serve({ Bun.serve({ port: 8000, fetch: behindProxy((request) => federation.fetch(request, { contextData: undefined }), fetch: behindProxy((request) => federation.fetch(request, { contextData: undefined })), }); ~~~~ ~~~~ typescript{1,5} [Bun] ~~~~ typescript{2,6} [Node.js] import { serve } from "@hono/node-server"; import { behindProxy } from "x-forwarded-fetch"; Bun.serve({ serve({ port: 8000, fetch: behindProxy((request) => federation.fetch(request, { contextData: undefined })), fetch: behindProxy((request) => federation.fetch(request, { contextData: undefined }), }); ~~~~ Loading @@ -610,12 +638,12 @@ then run the server again: deno run -A server.ts ~~~~ ~~~~ sh [Node.js] node --import tsx server.ts ~~~~ sh [Bun] bun run server.ts ~~~~ ~~~~ sh [Bun] bun server.ts ~~~~ sh [Node.js] node --import tsx server.ts ~~~~ ::: Loading Loading @@ -793,10 +821,12 @@ federation }); ~~~~ ~~~~ typescript{15-16,19-39} [Node.js] ~~~~ typescript{15-16,19-39} [Bun] import { serialize as encodeV8, deserialize as decodeV8 } from "node:v8"; import { openKv } from "@deno/kv"; const kv = await openKv("kv.db"); // Open the key-value store // Open the key-value store: const kv = await openKv("kv.db", { encodeV8, decodeV8 }); federation .setActorDispatcher("/users/{handle}", async (ctx, handle, key) => { Loading Loading @@ -838,12 +868,10 @@ federation }); ~~~~ ~~~~ typescript{15-16,19-39} [Bun] import { serialize as encodeV8, deserialize as decodeV8 } from "node:v8"; ~~~~ typescript{15-16,19-39} [Node.js] import { openKv } from "@deno/kv"; // Open the key-value store: const kv = await openKv("kv.db", { encodeV8, decodeV8 }); const kv = await openKv("kv.db"); // Open the key-value store federation .setActorDispatcher("/users/{handle}", async (ctx, handle, key) => { Loading Loading @@ -1039,8 +1067,8 @@ Deno.serve(async (request) => { }); ~~~~ ~~~~ typescript{4-18} [Node.js] serve({ ~~~~ typescript{4-18} [Bun] Bun.serve({ port: 8000, async fetch(request) { const url = new URL(request.url); Loading @@ -1065,8 +1093,8 @@ serve({ }); ~~~~ ~~~~ typescript{4-18} [Bun] Bun.serve({ ~~~~ typescript{4-18} [Node.js] serve({ port: 8000, async fetch(request) { const url = new URL(request.url); Loading Loading @@ -1143,12 +1171,16 @@ Exercises activity. - Integration with a web framework: In the above example, we hard-coded the home page inside the callback function passed to the `Deno.serve()`. Instead, you can use a web framework like [Fresh] to utilize the proper routing system and [JSX] templates to produce HTML. 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. [Hono]: https://hono.dev/ [Fresh]: https://fresh.deno.dev/ [JSX]: https://facebook.github.io/jsx/ Loading
docs/.vitepress/config.mts +5 −4 Original line number Diff line number Diff line Loading @@ -88,7 +88,7 @@ export default defineConfig({ link: "https://dash.deno.com/playground/fedify-demo", }, { text: "Installation", link: "/install.md" }, { text: "Tutorial", link: "/tutorial.md" }, { text: "In-depth tutorial", link: "/tutorial.md" }, { text: "CLI toolchain", link: "/cli.md", Loading Loading @@ -127,7 +127,8 @@ export default defineConfig({ }, { icon: { svg: '<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>ActivityPub</title><path d="M10.91 4.442L0 10.74v2.52L8.727 8.22v10.077l2.182 1.26zM6.545 12l-4.364 2.52 4.364 2.518zm6.545-2.52L17.455 12l-4.364 2.52zm0-5.038L24 10.74v2.52l-10.91 6.298v-2.52L21.819 12 13.091 6.96z"/></svg>', svg: '<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>ActivityPub</title><path d="M10.91 4.442L0 10.74v2.52L8.727 8.22v10.077l2.182 1.26zM6.545 12l-4.364 2.52 4.364 2.518zm6.545-2.52L17.455 12l-4.364 2.52zm0-5.038L24 10.74v2.52l-10.91 6.298v-2.52L21.819 12 13.091 6.96z"/></svg>', }, link: "https://hollo.social/@fedify", ariaLabel: "Hollo (ActivityPub)", Loading Loading @@ -181,8 +182,8 @@ export default defineConfig({ "meta", { name: "fediverse:creator", content: "@fedify@hollo.social" } content: "@fedify@hollo.social", }, ], ...plausibleScript, ], Loading
docs/install.md +68 −22 Original line number Diff line number Diff line Loading @@ -4,27 +4,77 @@ prev: text: What is Fedify? link: ./intro.md next: text: Tutorial text: In-depth tutorial link: ./tutorial.md --- Installation ============ Fedify is available on [JSR] for [Deno] and on [npm] for [Node.js] and [Bun]. > [!TIP] > We recommend using Deno or Bun (which are TypeScript-first) for the best > experience, but you can use Node.js if you prefer. 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 [Node.js] npm install -g @fedify/cli ~~~~ ~~~~ sh [Bun] bun install -g @fedify/cli ~~~~ ~~~~ sh [Deno] deno install -A --unstable-fs --unstable-kv --unstable-temporal -n fedify jsr:@fedify/cli ~~~~ ::: 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. 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 [Node.js]: https://nodejs.org/ [Bun]: https://bun.sh/ [Node.js]: https://nodejs.org/ Deno ---- ### Deno [Deno] is the primary runtime for Fedify. As a prerequisite, you need to have Deno 1.41.0 or later installed on your system. Then you can install Fedify Loading @@ -35,7 +85,7 @@ deno add @fedify/fedify ~~~~ Since Fedify requires [`Temporal`] API, which is an unstable feature in Deno as of May 2024, you need to add the `"temporal"` to the `"unstable"` field of of July 2024, you need to add the `"temporal"` to the `"unstable"` field of the *deno.json* file: ~~~~ json{5} Loading @@ -49,9 +99,16 @@ the *deno.json* file: [`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 ------- ### Node.js Fedify can also be used in Node.js. As a prerequisite, you need to have Node.js 20.0.0 or later installed on your system. Then you can install Fedify via Loading @@ -72,14 +129,3 @@ Fedify is an ESM-only package, so you need to add `"type": "module"` to the } } ~~~~ Bun --- Fedify can also be used in Bun. You can install it via the following command: ~~~~ sh bun add @fedify/fedify ~~~~
docs/manual/federation.md +77 −0 Original line number Diff line number Diff line Loading @@ -214,6 +214,83 @@ same path. Turned off by default. The `~Federation.fetch()` API ----------------------------- *This API is available since Fedify 0.6.0.* The `Federation` object provides the `~Federation.fetch()` method to handle incoming HTTP requests. The `~Federation.fetch()` method takes an incoming [`Request`] and returns a [`Response`]. Actually, this interface is de facto standard in the server-side JavaScript world, and it is inspired by the [`window.fetch()`] method in the browser environment. Therefore, you can pass it to the [`Deno.serve()`] function in [Deno], and the [`Bun.serve()`] function in [Bun]: ::: code-group ~~~~ typescript [Deno] Deno.serve( (request) => federation.fetch(request, { contextData: undefined }) ); ~~~~ ~~~~ typescript [Bun] Bun.serve({ fetch: (request) => federation.fetch(request, { contextData: undefined }), }) ~~~~ ::: However, in case of [Node.js], it has no built-in server API that takes `fetch()` callback function like Deno or Bun. Instead, you need to use [@hono/node-server] package to adapt the `~Federation.fetch()` method to the Node.js' HTTP server API: ::: code-group ~~~~ sh [Node.js] npm add @hono/node-server ~~~~ ::: And then, you can use the [`serve()`] function from the package: ::: code-group ~~~~ typescript [Node.js] import { serve } from "@hono/node-server"; serve({ fetch: (request) => federation.fetch(request, { contextData: undefined }), }) ~~~~ ::: > [!NOTE] > > Although a `Federation` object can be directly passed to the HTTP server > APIs, you would usually integrate it with a web framework. For details, > see the [*Integration* section](./integration.md). [`Request`]: https://developer.mozilla.org/en-US/docs/Web/API/Request [`Response`]: https://developer.mozilla.org/en-US/docs/Web/API/Response [`window.fetch()`]: https://developer.mozilla.org/en-US/docs/Web/API/Window/fetch [`Deno.serve()`]: https://docs.deno.com/api/deno/~/Deno.serve [Deno]: http://deno.com/ [`Bun.serve()`]: https://bun.sh/docs/api/http#bun-serve [Bun]: https://bun.sh/ [Node.js]: https://nodejs.org/ [@hono/node-server]: https://github.com/honojs/node-server [`serve()`]: https://github.com/honojs/node-server?tab=readme-ov-file#usage How the `Federation` object recognizes the domain name ------------------------------------------------------ Loading
docs/tutorial.md +110 −78 Original line number Diff line number Diff line Loading @@ -8,14 +8,20 @@ prev: link: ./install.md --- Tutorial ======== In-depth tutorial ================= In this tutorial, we will build a small federated server that can only accept follow requests. Despite its simplicity, it will cover the key features of the follow requests to understand the basic concepts of the Fedify framework. Despite its simplicity, it will cover the key features of the ActivityPub protocol and the Fedify framework, such as actors, sending and receiving activities, and the inbox. This tutorial will not use the quick start project template created by the [`fedify init`](./cli.md#fedify-init-initializing-a-fedify-project) command. Instead, we will start from scratch to understand how the Fedify framework works without any boilerplate code. As prerequisite knowledge, you should have a basic understanding of JavaScript, command-line interfaces, and minimum experience with building web server apps. However, it's perfectly fine if you're not familiar with Loading Loading @@ -50,18 +56,19 @@ echo '{ "unstable": ["kv", "temporal"] }' > deno.json deno add @fedify/fedify ~~~~ ~~~~ sh [Node.js] ~~~~ sh [Bun] mkdir follow-server cd follow-server/ echo '{ "type": "module" }' > package.json npm add -D typescript tsx @types/node npm add @deno/kv @fedify/fedify @hono/node-server bun add @deno/kv @fedify/fedify ~~~~ ~~~~ sh [Bun] ~~~~ sh [Node.js] mkdir follow-server cd follow-server/ bun add @deno/kv @fedify/fedify echo '{ "type": "module" }' > package.json npm add -D typescript tsx @types/node npm add @deno/kv @fedify/fedify @hono/node-server ~~~~ ::: Loading @@ -81,6 +88,16 @@ The above commands will create a *deno.json* (in case of Deno) or *package.json* } ~~~~ ~~~ json [Bun] { "type": "module", "dependencies": { "@deno/kv": "^0.8.0", "@fedify/fedify": "^0.12.0" } } ~~~ ~~~ json [Node.js] { "type": "module", Loading @@ -96,23 +113,34 @@ The above commands will create a *deno.json* (in case of Deno) or *package.json* } ~~~ ~~~ json [Bun] { "dependencies": { "@deno/kv": "^0.8.0", "@fedify/fedify": "^0.12.0" } } ~~~ ::: > [!NOTE] > The [`"unstable"`] field in the *deno.json* file is required because Fedify > uses [`Temporal`] API, which is an unstable feature in Deno as of > July 2024. By adding `"temporal"` to the `"unstable"` field, you can use the > Fedify framework without any issues. > [!NOTE] > In Bun and Node.js, you need to add [`"type": "module"`] to the *package.json* > file because Fedify is an ESM-only package. > [!TIP] > Do you wonder why we need to add *[tsx]* and *@types/node* in the case of > Node.js? It's because Fedify is written in TypeScript, and > Node.js doesn't support TypeScript out of the box. By adding *tsx* and > *@types/node*, you can write TypeScript code in Node.js without any hassle. [^2]: The actual version number may vary depending on the latest version of the Fedify framework as of reading this tutorial. [Deno]: https://deno.com/ [Bun]: https://bun.sh/ [Node.js]: https://nodejs.org/ [`"unstable"`]: https://docs.deno.com/runtime/manual/tools/unstable_flags/#configuring-flags-in-deno.json [`Temporal`]: https://tc39.es/proposal-temporal/docs/ [`"type": "module"`]: https://nodejs.org/api/packages.html#type [tsx]: https://tsx.is/ Creating the server Loading @@ -131,10 +159,8 @@ Deno.serve(request => ); ~~~~ ~~~~ typescript [Node.js] import { serve } from "@hono/node-server"; serve({ ~~~~ typescript [Bun] Bun.serve({ port: 8000, fetch(request) { return new Response("Hello, world", { Loading @@ -144,8 +170,10 @@ serve({ }); ~~~~ ~~~~ typescript [Bun] Bun.serve({ ~~~~ typescript [Node.js] import { serve } from "@hono/node-server"; serve({ port: 8000, fetch(request) { return new Response("Hello, world", { Loading @@ -166,30 +194,30 @@ request. You can run the server by executing the following command: deno run -A server.ts ~~~~ ~~~~ sh [Node.js] node --import tsx server.ts ~~~~ ~~~~ sh [Bun] bun server.ts ~~~~ ~~~~ sh [Node.js] node --import tsx server.ts ~~~~ :::: Now, open your web browser and navigate to <http://localhost:8000/>. You should see the <q>Hello, world</q> message. As you can guess, [`Deno.serve()`] (in case of Deno), [`serve()`] (in case of Node.js), and [`Bun.serve()`] (in case of Bun) are a function to create an HTTP As you can guess, [`Deno.serve()`] (in case of Deno), [`Bun.serve()`] (in case of Bun), and [`serve()`] (in case of Node.js) are a function to create an HTTP server. They take a callback function that receives a [`Request`] object and returns a [`Response`] object. The `Response` object is sent back to the client. This server is not federated yet, but it's a good starting point to build a federated server. [`Deno.serve()`]: https://deno.land/api?s=Deno.serve [`serve()`]: https://github.com/honojs/node-server?tab=readme-ov-file#usage [`Deno.serve()`]: https://docs.deno.com/api/deno/~/Deno.serve [`Bun.serve()`]: https://bun.sh/docs/api/http#bun-serve [`serve()`]: https://github.com/honojs/node-server?tab=readme-ov-file#usage [`Request`]: https://developer.mozilla.org/en-US/docs/Web/API/Request [`Response`]: https://developer.mozilla.org/en-US/docs/Web/API/Response Loading Loading @@ -232,10 +260,8 @@ Deno.serve( ); ~~~~ ~~~~ typescript{6} [Node.js] import { serve } from "@hono/node-server"; serve({ ~~~~ typescript{4} [Bun] Bun.serve({ port: 8000, fetch(request) { return federation.fetch(request, { contextData: undefined }); Loading @@ -243,8 +269,10 @@ serve({ }); ~~~~ ~~~~ typescript{4} [Bun] Bun.serve({ ~~~~ typescript{6} [Node.js] import { serve } from "@hono/node-server"; serve({ port: 8000, fetch(request) { return federation.fetch(request, { contextData: undefined }); Loading Loading @@ -283,14 +311,14 @@ to the next step. > deno add @logtape/logtape > ~~~~ > > ~~~~ sh [Node.js] > npm add @logtape/logtape > ~~~~ > > ~~~~ sh [Bun] > bun add @logtape/logtape > ~~~~ > > ~~~~ sh [Node.js] > npm add @logtape/logtape > ~~~~ > > ::: > > Then, you can set up loggers by calling [`configure()`] function at the Loading Loading @@ -352,9 +380,8 @@ Deno.serve( ); ~~~~ ~~~~ typescript{8-17} [Node.js] ~~~~ typescript{7-16} [Bun] import { Federation, MemoryKvStore, Person } from "@fedify/fedify"; import { serve } from "@hono/node-server"; const federation = createFederation<void>({ kv: new MemoryKvStore(), Loading @@ -371,7 +398,7 @@ federation.setActorDispatcher("/users/{handle}", async (ctx, handle) => { }); }); serve({ Bun.serve({ port: 8000, fetch(request) { return federation.fetch(request, { contextData: undefined }); Loading @@ -379,8 +406,9 @@ serve({ }); ~~~~ ~~~~ typescript{7-16} [Bun] ~~~~ typescript{8-17} [Node.js] import { Federation, MemoryKvStore, Person } from "@fedify/fedify"; import { serve } from "@hono/node-server"; const federation = createFederation<void>({ kv: new MemoryKvStore(), Loading @@ -397,7 +425,7 @@ federation.setActorDispatcher("/users/{handle}", async (ctx, handle) => { }); }); Bun.serve({ serve({ port: 8000, fetch(request) { return federation.fetch(request, { contextData: undefined }); Loading @@ -423,12 +451,12 @@ WebFinger for the actor. Run the server by executing the following command: deno run -A server.ts ~~~~ ~~~~ sh [Node.js] node --import tsx server.ts ~~~~ sh [Bun] bun run server.ts ~~~~ ~~~~ sh [Bun] bun server.ts ~~~~ sh [Node.js] node --import tsx server.ts ~~~~ ::: Loading Loading @@ -557,14 +585,14 @@ To do this, you need to install the package: deno add @hongminhee/x-forwarded-fetch ~~~~ ~~~~ sh [Node.js] npm install x-forwarded-fetch ~~~~ ~~~~ sh [Bun] bun add x-forwarded-fetch ~~~~ ~~~~ sh [Node.js] npm add x-forwarded-fetch ~~~~ ::: Then, import the package and place the `behindProxy()` middleware in front of Loading @@ -580,22 +608,22 @@ Deno.serve( ); ~~~~ ~~~~ typescript{2,6} [Node.js] import { serve } from "@hono/node-server"; ~~~~ typescript{1,5} [Bun] import { behindProxy } from "x-forwarded-fetch"; serve({ Bun.serve({ port: 8000, fetch: behindProxy((request) => federation.fetch(request, { contextData: undefined }), fetch: behindProxy((request) => federation.fetch(request, { contextData: undefined })), }); ~~~~ ~~~~ typescript{1,5} [Bun] ~~~~ typescript{2,6} [Node.js] import { serve } from "@hono/node-server"; import { behindProxy } from "x-forwarded-fetch"; Bun.serve({ serve({ port: 8000, fetch: behindProxy((request) => federation.fetch(request, { contextData: undefined })), fetch: behindProxy((request) => federation.fetch(request, { contextData: undefined }), }); ~~~~ Loading @@ -610,12 +638,12 @@ then run the server again: deno run -A server.ts ~~~~ ~~~~ sh [Node.js] node --import tsx server.ts ~~~~ sh [Bun] bun run server.ts ~~~~ ~~~~ sh [Bun] bun server.ts ~~~~ sh [Node.js] node --import tsx server.ts ~~~~ ::: Loading Loading @@ -793,10 +821,12 @@ federation }); ~~~~ ~~~~ typescript{15-16,19-39} [Node.js] ~~~~ typescript{15-16,19-39} [Bun] import { serialize as encodeV8, deserialize as decodeV8 } from "node:v8"; import { openKv } from "@deno/kv"; const kv = await openKv("kv.db"); // Open the key-value store // Open the key-value store: const kv = await openKv("kv.db", { encodeV8, decodeV8 }); federation .setActorDispatcher("/users/{handle}", async (ctx, handle, key) => { Loading Loading @@ -838,12 +868,10 @@ federation }); ~~~~ ~~~~ typescript{15-16,19-39} [Bun] import { serialize as encodeV8, deserialize as decodeV8 } from "node:v8"; ~~~~ typescript{15-16,19-39} [Node.js] import { openKv } from "@deno/kv"; // Open the key-value store: const kv = await openKv("kv.db", { encodeV8, decodeV8 }); const kv = await openKv("kv.db"); // Open the key-value store federation .setActorDispatcher("/users/{handle}", async (ctx, handle, key) => { Loading Loading @@ -1039,8 +1067,8 @@ Deno.serve(async (request) => { }); ~~~~ ~~~~ typescript{4-18} [Node.js] serve({ ~~~~ typescript{4-18} [Bun] Bun.serve({ port: 8000, async fetch(request) { const url = new URL(request.url); Loading @@ -1065,8 +1093,8 @@ serve({ }); ~~~~ ~~~~ typescript{4-18} [Bun] Bun.serve({ ~~~~ typescript{4-18} [Node.js] serve({ port: 8000, async fetch(request) { const url = new URL(request.url); Loading Loading @@ -1143,12 +1171,16 @@ Exercises activity. - Integration with a web framework: In the above example, we hard-coded the home page inside the callback function passed to the `Deno.serve()`. Instead, you can use a web framework like [Fresh] to utilize the proper routing system and [JSX] templates to produce HTML. 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. [Hono]: https://hono.dev/ [Fresh]: https://fresh.deno.dev/ [JSX]: https://facebook.github.io/jsx/