express.router()でNext.jsのページを表示しようとするとうまくいかない
公開日:
はじめに
Next.js
のカスタムサーバーとして Express
を利用して、Next.js
で SSR しようとしたらうまく Next.js
のルーティングが機能しないことがあったので解決法を記事にしました。
結論
Next.js
でカスタムサーバーを用いてページを SSR したい時は、
app.use(
'/_private-legacy-pages',
express.Router().get('/', next.delegateToNext),
);
上のように express
の router
機能を使うんじゃなくて
app.get('/_private-legacy-pages', next.delegateToNext);
純粋なget
を使いましょう。
やりたいこと
該当部分のコード
app.use(
'/_private-legacy-pages',
express.Router().get('/', next.delegateToNext),
);
やりたいことは
/_private-legacy-pages
にリクエストが来た時に
└─page
├── _private-legacy-pages.page.tsx // こっちのファイルのページが表示されてほしい
└── [[...path]].page.tsx // 今の実装だと、こっちが表示されてしまう
上のように、page ディレクトリ下にある _private-legacy-pages.page.tsxを SSR してブラウザに表示することです。
該当部分のコードのnext.delegateToNext
というミドルウェアは以下のコードで
const { nextApp } = crowi;
const handle = nextApp.getRequestHandler();
const delegateToNext = (req: CrowiReq, res: Response): void => {
req.crowi = crowi;
return handle(req, res);
};
return {
delegateToNext,
};
実質的には、公式ドキュメントにあるように、
Next サーバーのgetRequestHandler
を呼んでいるだけなので、細かい実装は気にしないでください。
page ディレクトリ下には、どのルーティングにも当てはまらないパスを処理する[[..path]].page.tsx
というファイルがあるのですが、先ほどのルーティングのままだとブラウザに _private-legacy-pagesを打ち込むと、_private-legacy-pages.page.tsxが page ディレクトリ下に存在するにもかかわらず、[[..path]].page.tsx
の方のページが表示されてしまいます。
解決法
app.use(
'/_private-legacy-pages',
express.Router().get('/', next.delegateToNext),
);
上のように express
の router
機能を使うんじゃなくて
app.get('/_private-legacy-pages', next.delegateToNext);
純粋な get
を使ったルーティングで実装したところ、正常にルーティングをすることができました。
原因
原因は正直なところわかりません。
Express
の router()
はルーティングをファイル分割できるのが旨味であって
app.use('/_private-legacy-pages', express.Router().get('/', hoge));
と
app.get('/_private-legacy-pages', hoge);
は同値だと考えてきたので、この二つで挙動の違いが出ることが謎です。
https://stackoverflow.com/questions/28305120/differences-between-express-router-and-app-get
ここらへんの記事で議論されてるっぽいんですが、おそらく Next の内部で何かしら処理の違いが出ているのではという予想です。
正常に動作はするので許してください
最後に
1 日悩んだことを、他の人に相談したら 10 分くらいで解決しました。真理ですね。
では
Bye