NextJSとの併用
競合を避けるため、使用するルーターに関わらず、appとpagesの両方のFSD層を_appと_pagesに名前変更してください。
srcフォルダー
NextJSは、プロジェクトのルートまたはsrcフォルダーのいずれかに、特別なappまたはpagesフォルダーを期待します。一般的には、srcフォルダーにFSDコードのみが含まれるように、NextJSのフォルダーをプロジェクトのルートに配置する方が簡単ですが、必須ではありません。
App Router
NextJSはApp Routerにappフォルダーを、Pages Routerにpagesフォルダーを使用しますが、これはFSDの層名と競合します。この競合を解決するには、appの代わりに_app、pagesの代わりに_pagesのように、FSD層にプレフィックス付きの名前を使用してください。このアプローチは公式のリンターとも互換性があります。
├── app # NextJSのappフォルダー
│ ├── api
│ │ └── get-example
│ │ └── route.ts
│ └── example
│ └── page.tsx
└── src
├── _app # FSD層
│ └── api-routes # APIルート
├── _pages # FSD層
│ └── example
│ ├── index.ts
│ └── ui
│ └── example.tsx
├── widgets
├── features
├── entities
└── shared
NextJSのapp内でsrc/_pagesからページを再エクスポートする例:
export { ExamplePage as default, metadata } from '@/_pages/example';
サーバーとクライアントのパブリックAPI
NextJSのApp Routerでは、クライアントで使用できるモジュールとサーバー専用のモジュールが、1つのスライス内に共存することがあります。サーバー専用のモジュールがindex.tsからエクスポートされている場合、クライアントコンポーネントがそのスライスをインポートすると、サーバー専用の副作用がクライアントのモジュールグラフに伝播し、ビルドエラーにつながる可能性があります。
この問題が発生した場合は、パブリックAPIにindex.server.tsを追加してください。
index.server.ts: サーバーコンポーネントやserver-onlyでマークされたデータアクセス関数など、サーバーでのみインポートされる必要があるモジュール
Middleware
プロジェクトでミドルウェアを使用する場合は、NextJSのappおよびpagesフォルダーと並んでプロジェクトのルートに配置する必要があります。
Instrumentation
instrumentation.jsファイルを使用すると、アプリケーションのパフォーマンスと動作を監視できます。使用する場合は、middleware.jsと同様にプロジェクトのルートに配置する必要があります。
Pages Router
pages層におけるFSDとNextJSの競合
App Routerのappフォルダーと同様に、ルートはプロジェクトのルートにあるpagesフォルダーに配置する必要があります。層フォルダーが配置されているsrc内の構造は変わりません。
├── pages # pagesフォルダー(NextJS)
│ ├── _app.tsx
│ ├── api
│ │ └── example.ts # APIルートの再エクスポート
│ └── example
│ └── index.tsx
└── src
├── _app # FSD層
│ ├── custom-app
│ │ └── custom-app.tsx # カスタムAppコンポーネント
│ └── api-routes
│ └── get-example-data.ts # APIルート
├── _pages # FSD層
│ └── example
│ ├── index.ts
│ └── ui
│ └── example.tsx
├── widgets
├── features
├── entities
└── shared
NextJSのpages内でsrc/_pagesからページを再エクスポートする例:
export { Example as default } from '@/_pages/example';
カスタム_appコンポーネント
カスタムAppコンポーネントはsrc/_app/_appまたはsrc/_app/custom-appに配置できます:
import type { AppProps } from 'next/app';
export const MyApp = ({ Component, pageProps }: AppProps) => {
return (
<>
<p>My Custom App component</p>
<Component { ...pageProps } />
</>
);
};
export { App as default } from '@/_app/custom-app';
ルートハンドラー(APIルート)
ルートハンドラーを扱うには、_app層のapi-routesセグメントを使用します。
FSD構造内でバックエンドのコードを書く際は注意してください。FSDは主にフロントエンドを対象としており、それが人々が見つけることを期待するものだからです。 多くのエンドポイントが必要な場合は、モノレポ内の別のパッケージに分離することを検討してください。
- App Router
- Pages Router
import { getExamplesList } from '@/shared/db';
export const getExampleData = () => {
try {
const examplesList = getExamplesList();
return Response.json({ examplesList });
} catch {
return Response.json(null, {
status: 500,
statusText: 'Ouch, something went wrong',
});
}
};
export { getExampleData as GET } from '@/_app/api-routes';
import type { NextApiRequest, NextApiResponse } from 'next';
const config = {
api: {
bodyParser: {
sizeLimit: '1mb',
},
},
maxDuration: 5,
};
const handler = (req: NextApiRequest, res: NextApiResponse<ResponseData>) => {
res.status(200).json({ message: 'Hello from FSD' });
};
export const getExampleData = { config, handler } as const;
export { getExampleData } from './get-example-data';
import { getExampleData } from '@/_app/api-routes';
export const config = getExampleData.config;
export default getExampleData.handler;
追加の推奨事項
- データベースクエリの記述と上位層でのその利用には、
shared層のdbセグメントを使用してください。 - クエリのキャッシュと再検証のロジックは、クエリ自体と同じ場所に保持する方が良いでしょう。