express.router()でNext.jsのページを表示しようとするとうまくいかない

公開日: 

はじめに

Next.js のカスタムサーバーとして Express を利用して、Next.js で SSR しようとしたらうまく Next.js のルーティングが機能しないことがあったので解決法を記事にしました。

結論

Next.js でカスタムサーバーを用いてページを SSR したい時は、

bad.js
app.use(
  '/_private-legacy-pages',
  express.Router().get('/', next.delegateToNext),
);

上のように expressrouter 機能を使うんじゃなくて

good.js
app.get('/_private-legacy-pages', next.delegateToNext);

純粋なgetを使いましょう。

やりたいこと

該当部分のコード

sample.js
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というミドルウェアは以下のコードで

middleware.js
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 の方のページが表示されてしまいます。

解決法

bad.js
app.use(
  '/_private-legacy-pages',
  express.Router().get('/', next.delegateToNext),
);

上のように expressrouter 機能を使うんじゃなくて

good.js
app.get('/_private-legacy-pages', next.delegateToNext);

純粋な get を使ったルーティングで実装したところ、正常にルーティングをすることができました。

原因

原因は正直なところわかりません。

Expressrouter()はルーティングをファイル分割できるのが旨味であって

router.js
app.use('/_private-legacy-pages', express.Router().get('/', hoge));

get.js
app.get('/_private-legacy-pages', hoge);

は同値だと考えてきたので、この二つで挙動の違いが出ることが謎です。

https://stackoverflow.com/questions/28305120/differences-between-express-router-and-app-get

https://stackoverflow.com/questions/70866829/express-route-and-nextjs-app-render-doesnt-work-in-router-file

ここらへんの記事で議論されてるっぽいんですが、おそらく Next の内部で何かしら処理の違いが出ているのではという予想です。

正常に動作はするので許してください

最後に

1 日悩んだことを、他の人に相談したら 10 分くらいで解決しました。真理ですね。

では

Bye