UNPKG

@fedify/next

Version:

Integrate Fedify with Next.js

96 lines (94 loc) 3.52 kB
import { notFound } from "next/navigation"; import { NextResponse } from "next/server"; //#region src/index.ts /** * Wrapper function for Next.js middleware to integrate with the * {@link Federation} object. * * @template TContextData A type of the context data for the * {@link Federation} object. * @param federation A {@link Federation} object to integrate with Next.js. * @param contextDataFactory A function to create a context data for the * {@link Federation} object. * @param errorHandlers A set of error handlers to handle errors during * the federation fetch. * @returns A Next.js middleware function to integrate with the * {@link Federation} object. * * @example * ```ts ignore * import { fedifyWith } from "@fedify/next"; * import { federation } from "./federation"; * * export default fedifyWith(federation)( * function (request: Request) { * // You can add custom logic here for other requests * // except federation requests. If there is no custom logic, * // you can omit this function. * } * ) * * // This config makes middleware process only requests with the * // "Accept" header matching the federation accept regex. * // More details: https://nextjs.org/docs/app/api-reference/file-conventions/middleware#config-object-optional. * export const config = { * runtime: "nodejs", * matcher: [{ * source: "/:path*", * has: [ * { * type: "header", * key: "Accept", * value: ".*application\\/((jrd|activity|ld)\\+json|xrd\\+xml).*", * }, * ], * }], * }; * ``` */ const fedifyWith = (federation, contextDataFactory, errorHandlers) => (middleware = (_) => NextResponse.next()) => async (request) => { if (hasFederationAcceptHeader(request)) return await integrateFederation(federation, contextDataFactory, errorHandlers)(request); return await middleware(request); }; /** * Check if the request has the "Accept" header matching the federation * accept regex. * * @param request The request to check. * @returns `true` if the request has the "Accept" header matching * the federation accept regex, `false` otherwise. */ const hasFederationAcceptHeader = (request) => { const acceptHeader = request.headers.get("Accept"); return acceptHeader ? FEDERATION_ACCEPT_REGEX.test(acceptHeader) : false; }; const FEDERATION_ACCEPT_REGEX = /.*application\/((jrd|activity|ld)\+json|xrd\+xml).*/; /** * Create a Next.js handler to integrate with the {@link Federation} object. * * @template TContextData A type of the context data for the * {@link Federation} object. * @param federation A {@link Federation} object to integrate with Next.js. * @param contextDataFactory A function to create a context data for the * {@link Federation} object. * @param errorHandlers A set of error handlers to handle errors during * the federation fetch. * @returns A Next.js handler. */ function integrateFederation(federation, contextDataFactory = () => void 0, errorHandlers) { return async (request) => await federation.fetch(request, { contextData: await contextDataFactory(request), onNotFound: notFound, onNotAcceptable, ...errorHandlers }); } const onNotAcceptable = () => new Response("Not acceptable", { status: 406, headers: { "Content-Type": "text/plain", Vary: "Accept" } }); //#endregion export { fedifyWith, hasFederationAcceptHeader, integrateFederation };