Skip to content

Commit 7f76abd

Browse files
committed
Rename @fedify/nextjs to @fedify/next
fedify-dev#355 (comment) Rename `@fedify/nextjs` to `@fedify/next` fedify-dev#355 (comment) Rename `@fedify/nextjs` to `@fedify/next` fedify-dev#355 (comment) Rename `@fedify/nextjs` to `@fedify/next` fedify-dev#355 (comment) Rename `@fedify/nextjs` to `@fedify/next` fedify-dev#355 (comment)
1 parent 0b8a84c commit 7f76abd

File tree

14 files changed

+292
-4
lines changed

14 files changed

+292
-4
lines changed

.github/workflows/build.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,7 @@ jobs:
425425
| @fedify/express | ${{ steps.versioning.outputs.version }} | [JSR][jsr:@fedify/express] | [npm][npm:@fedify/express] |
426426
| @fedify/h3 | ${{ steps.versioning.outputs.version }} | [JSR][jsr:@fedify/h3] | [npm][npm:@fedify/h3] |
427427
| @fedify/nestjs | ${{ steps.versioning.outputs.version }} | | [npm][npm:@fedify/nestjs] |
428+
| @fedify/next | ${{ steps.versioning.outputs.version }} | | [npm][npm:@fedify/next] |
428429
| @fedify/postgres | ${{ steps.versioning.outputs.version }} | [JSR][jsr:@fedify/postgres] | [npm][npm:@fedify/postgres] |
429430
| @fedify/redis | ${{ steps.versioning.outputs.version }} | [JSR][jsr:@fedify/redis] | [npm][npm:@fedify/redis] |
430431
| @fedify/sqlite | ${{ steps.versioning.outputs.version }} | [JSR][jsr:@fedify/sqlite] | [npm][npm:@fedify/sqlite] |

AGENTS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ The repository is organized as a monorepo with the following packages:
7070
- *packages/postgres/*: PostgreSQL drivers (@fedify/postgres)
7171
- *packages/redis/*: Redis drivers (@fedify/redis)
7272
- *packages/nestjs/*: NestJS integration (@fedify/nestjs)
73+
- *packages/next/*: Next.js integration (@fedify/next)
7374
- *packages/sqlite/*: SQLite driver (@fedify/sqlite)
7475
- *packages/testing/*: Testing utilities (@fedify/testing)
7576
- *docs/*: Documentation built with Node.js and VitePress

CHANGES.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,21 @@ Version 1.8.6
88

99
To be released.
1010

11+
### @fedify/next
12+
13+
- Created [Next.js] integration as the *@fedify/next* package.
14+
[[#313] by Chanhaeng Lee]
15+
16+
### @fedify/cli
17+
18+
- Added `Next.js` option to `fedify init` command. This option allows users
19+
to initialize a new Fedify project with Next.js integration.
20+
[[#313] by Chanhaeng Lee]
21+
22+
23+
[Next.js]: https://nextjs.org/
24+
[#313]: https://github.com/fedify-dev/fedify/issues/313
25+
1126

1227
Version 1.8.5
1328
-------------

CONTRIBUTING.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,7 @@ The repository is organized as a monorepo with the following packages:
193193
- *packages/postgres/*: PostgreSQL drivers (@fedify/postgres) for Fedify.
194194
- *packages/redis/*: Redis drivers (@fedify/redis) for Fedify.
195195
- *packages/nestjs/*: NestJS integration (@fedify/nestjs) for Fedify.
196+
- *packages/next/*: Next.js integration (@fedify/next) for Fedify.
196197
- *packages/sqlite/*: SQLite driver (@fedify/sqlite) for Fedify.
197198
- *packages/testing/*: Testing utilities (@fedify/testing) for Fedify.
198199
- *docs/*: The Fedify docs. The docs are built with [Node.js] and

docs/manual/integration.md

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,81 @@ console.log("Elysia App Start!");
480480
[Elysia]: https://elysiajs.com/
481481

482482

483+
Next.js
484+
------
485+
486+
*This API is available since Fedify 1.9.0.*
487+
488+
[Next.js] is a React framework that enables you to build server-rendered
489+
and statically generated web applications. Fedify has the `@fedify/next`
490+
module that provides a middleware to integrate Fedify with Next.js. Create
491+
an app with the following command using the Fedify CLI:
492+
493+
~~~~ sh
494+
fedify init my-next-app
495+
496+
? Choose the JavaScript runtime to use › Node.js
497+
? Choose the package manager to use › npm
498+
? Choose the web framework to integrate Fedify with › Next.js
499+
? Choose the key–value store to use for caching › In-memory
500+
? Choose the message queue to use for background jobs › In-process
501+
✔ Would you like your code inside a `src/` directory? … No
502+
✔ Would you like to customize the import alias (`@/*` by default)? … No
503+
~~~~
504+
505+
Then you can see the Next.js boilerplate code in the `my-next-app` directory.
506+
But if you created a Next.js app with `create-next-app` before, you'll see
507+
some differences in the code. There is a `middleware.ts` file in the
508+
`my-next-app` directory, which is the entry point to the Fedify middleware
509+
from the Next.js framework:
510+
511+
512+
~~~~ typescript
513+
import { fedifyWith } from "@fedify/next";
514+
import federation from "./federation";
515+
516+
export default fedifyWith(federation)(
517+
/*
518+
function (request: Request) {
519+
// If you need to handle other requests besides federation
520+
// requests in middleware, you can do it here.
521+
// If you handle only federation requests in middleware,
522+
// you don't need this function.
523+
return NextResponse.next();
524+
},
525+
*/
526+
)
527+
528+
// This config needs because middleware process only requests with the
529+
// "Accept" header matching the federation accept regex.
530+
// More details: https://nextjs.org/docs/app/api-reference/file-conventions/middleware#config-object-optional
531+
export const config = {
532+
runtime: "nodejs",
533+
matcher: [{
534+
source: "/:path*",
535+
has: [
536+
{
537+
type: "header",
538+
key: "Accept",
539+
value: ".*application\\\\/((jrd|activity|ld)\\\\+json|xrd\\\\+xml).*",
540+
},
541+
],
542+
}],
543+
};
544+
~~~~
545+
546+
As you can see in the comment, you can handle other requests besides
547+
federation requests in the middleware. If you handle only federation requests
548+
in the middleware, you can omit the function argument of `fedifyWith()`.
549+
The `config` object is necessary to let Next.js know that the middleware
550+
should process requests with the `Accept` header matching the federation
551+
accept regex. This is because Next.js middleware processes only requests
552+
with the `Accept` header matching the regex by default. More details can be
553+
found in the [Next.js documentation](https://nextjs.org/docs/app/api-reference/file-conventions/middleware#config-object-optional).
554+
555+
[Next.js]: https://nextjs.org/
556+
557+
483558
Custom middleware
484559
-----------------
485560

docs/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
"@fedify/fedify": "workspace:",
99
"@fedify/h3": "workspace:",
1010
"@fedify/nestjs": "workspace:",
11+
"@fedify/next": "workspace:",
1112
"@fedify/postgres": "workspace:",
1213
"@fedify/redis": "workspace:",
1314
"@fedify/sqlite": "workspace:",

packages/cli/src/init.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ const packagesMetaData: Record<`@fedify/${string}`, string> = {
1414
"@fedify/amqp": metadata.version,
1515
"@fedify/express": metadata.version,
1616
"@fedify/h3": metadata.version,
17+
"@fedify/next": metadata.version,
1718
};
1819

1920
const logger = getLogger(["fedify", "cli", "init"]);

packages/fedify/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ Here is the list of packages:
9999
| [@fedify/express](/packages/express/) | [JSR][jsr:@fedify/express] | [npm][npm:@fedify/express] | Express integration |
100100
| [@fedify/h3](/packages/h3/) | [JSR][jsr:@fedify/h3] | [npm][npm:@fedify/h3] | H3 integration |
101101
| [@fedify/nestjs](/packages/nestjs/) | | [npm][npm:@fedify/nestjs] | NestJS integration |
102+
| [@fedify/next](/packages/next/) | | | Next.js integration |
102103
| [@fedify/postgres](/packages/postgres/) | [JSR][jsr:@fedify/postgres] | [npm][npm:@fedify/postgres] | PostgreSQL driver |
103104
| [@fedify/redis](/packages/redis/) | [JSR][jsr:@fedify/redis] | [npm][npm:@fedify/redis] | Redis driver |
104105
| [@fedify/sqlite](/packages/sqlite/) | [JSR][jsr:@fedify/sqlite] | [npm][npm:@fedify/sqlite] | SQLite driver |

packages/next/README.md

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
<!-- deno-fmt-ignore-file -->
2+
3+
@fedify/next: Integrate Fedify with Next.js
4+
===========================================
5+
6+
[![Follow @fedify@hollo.social][@[email protected] badge]][@[email protected]]
7+
8+
This package provides a simple way to integrate [Fedify] with [Next.js].
9+
10+
11+
### Usage
12+
13+
~~~~ typescript
14+
// --- middleware.ts ---
15+
import { fedifyWith } from "@fedify/next";
16+
import { federation } from "./federation";
17+
18+
export default fedifyWith(federation)();
19+
20+
// This config must be defined on `middleware.ts`.
21+
export const config = {
22+
runtime: "nodejs",
23+
matcher: [{
24+
source: "/:path*",
25+
has: [
26+
{
27+
type: "header",
28+
key: "Accept",
29+
value: ".*application\\/((jrd|activity|ld)\\+json|xrd\\+xml).*",
30+
},
31+
],
32+
}],
33+
};
34+
~~~~
35+
36+
37+
The integration code looks like this:
38+
39+
40+
~~~~ typescript
41+
/**
42+
* Fedify with Next.js
43+
* ================
44+
*
45+
* This module provides a [Next.js] middleware to integrate with the Fedify.
46+
*
47+
* [Next.js]: https://nextjs.org/
48+
*
49+
* @module
50+
* @since 1.9.0
51+
*/
52+
import type { Federation, FederationFetchOptions } from "@fedify/fedify";
53+
import { notFound } from "next/navigation";
54+
import { NextResponse } from "next/server";
55+
import { getXForwardedRequest } from "x-forwarded-fetch";
56+
57+
interface ContextDataFactory<TContextData> {
58+
(request: Request):
59+
| TContextData
60+
| Promise<TContextData>;
61+
}
62+
type ErrorHandlers = Omit<FederationFetchOptions<unknown>, "contextData">;
63+
64+
/**
65+
* Wrapper function for Next.js middleware to integrate with the
66+
* {@link Federation} object.
67+
*
68+
* @template TContextData A type of the context data for the
69+
* {@link Federation} object.
70+
* @param {Federation<TContextData>} federation A {@link Federation} object
71+
* to integrate with Next.js.
72+
* @param {ContextDataFactory<TContextData>} contextDataFactory A function
73+
* to create a context data for the
74+
* {@link Federation} object.
75+
* @param {Partial<ErrorHandlers>} errorHandlers A set of error handlers to
76+
* handle errors during the federation fetch.
77+
* @returns A Next.js middleware function to integrate with the
78+
* {@link Federation} object.
79+
*
80+
* @example
81+
* ```ts
82+
* import { fedifyWith } from "@fedify/next";
83+
* import { federation } from "./federation";
84+
*
85+
* export default fedifyWith(federation)(
86+
* function (request: Request) {
87+
* // You can add custom logic here for other requests
88+
* // except federation requests. If there is no custom logic,
89+
* // you can omit this function.
90+
* }
91+
* )
92+
*
93+
* // This config makes middleware process only requests with the
94+
* // "Accept" header matching the federation accept regex.
95+
* // More details: https://nextjs.org/docs/app/api-reference/file-conventions/middleware#config-object-optional.
96+
* export const config = {
97+
* runtime: "nodejs",
98+
* matcher: [{
99+
* source: "/:path*",
100+
* has: [
101+
* {
102+
* type: "header",
103+
* key: "Accept",
104+
* value: ".*application\\/((jrd|activity|ld)\\+json|xrd\\+xml).*",
105+
* },
106+
* ],
107+
* }],
108+
* };
109+
* ```
110+
*/
111+
export const fedifyWith = <TContextData>(
112+
federation: Federation<TContextData>,
113+
contextDataFactory?: ContextDataFactory<TContextData>,
114+
errorHandlers?: Partial<ErrorHandlers>,
115+
) =>
116+
(
117+
middleware: (request: Request) => unknown =
118+
((_: Request) => NextResponse.next()),
119+
): (request: Request) => unknown =>
120+
async (request: Request) => {
121+
if (hasFederationAcceptHeader(request)) {
122+
return await integrateFederation(
123+
federation,
124+
contextDataFactory,
125+
errorHandlers,
126+
)(request);
127+
}
128+
return await middleware(request);
129+
};
130+
131+
/**
132+
* Check if the request has the "Accept" header matching the federation
133+
* accept regex.
134+
*
135+
* @param {Request} request The request to check.
136+
* @returns {boolean} `true` if the request has the "Accept" header matching
137+
* the federation accept regex, `false` otherwise.
138+
*/
139+
export const hasFederationAcceptHeader = (request: Request): boolean => {
140+
const acceptHeader = request.headers.get("Accept");
141+
// Check if the Accept header matches the federation accept regex.
142+
// If the header is not present, return false.
143+
return acceptHeader ? FEDERATION_ACCEPT_REGEX.test(acceptHeader) : false;
144+
};
145+
const FEDERATION_ACCEPT_REGEX =
146+
/.*application\/((jrd|activity|ld)\+json|xrd\+xml).*/;
147+
148+
/**
149+
* Create a Next.js handler to integrate with the {@link Federation} object.
150+
*
151+
* @template TContextData A type of the context data for the
152+
* {@link Federation} object.
153+
* @param {Federation<TContextData>} federation A {@link Federation} object
154+
* to integrate with Next.js.
155+
* @param {ContextDataFactory<TContextData>} contextDataFactory A function
156+
* to create a context data for the
157+
* {@link Federation} object.
158+
* @param {Partial<ErrorHandlers>} errorHandlers A set of error handlers to
159+
* handle errors during the federation fetch.
160+
* @returns A Next.js handler.
161+
*/
162+
export function integrateFederation<TContextData>(
163+
federation: Federation<TContextData>,
164+
contextDataFactory: ContextDataFactory<TContextData> = () =>
165+
undefined as TContextData,
166+
errorHandlers?: Partial<ErrorHandlers>,
167+
) {
168+
return async (request: Request) => {
169+
const forwardedRequest = await getXForwardedRequest(request);
170+
const contextData = await contextDataFactory(forwardedRequest);
171+
return await federation.fetch(
172+
forwardedRequest,
173+
{
174+
contextData,
175+
onNotFound: notFound,
176+
onNotAcceptable,
177+
...errorHandlers,
178+
},
179+
);
180+
};
181+
}
182+
const onNotAcceptable = () =>
183+
new Response("Not acceptable", {
184+
status: 406,
185+
headers: { "Content-Type": "text/plain", Vary: "Accept" },
186+
});
187+
~~~~
188+
189+
[@[email protected] badge]: https://fedi-badge.deno.dev/@[email protected]/followers.svg
190+
[@[email protected]]: https://hollo.social/@fedify
191+
[Fedify]: https://fedify.dev/
192+
[Next.js]: https://nextjs.org/

packages/nextjs/package.json renamed to packages/next/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"name": "@fedify/nextjs",
2+
"name": "@fedify/next",
33
"version": "1.9.0",
44
"description": "Integrate Fedify with Next.js",
55
"keywords": [
@@ -18,7 +18,7 @@
1818
"repository": {
1919
"type": "git",
2020
"url": "git+https://github.com/fedify-dev/fedify.git",
21-
"directory": "packages/nextjs"
21+
"directory": "packages/next"
2222
},
2323
"license": "MIT",
2424
"bugs": {

0 commit comments

Comments
 (0)