🦔

【Next.js】getServerSidePropsをリバースプロキシ化して遊ぼう

2021/09/21に公開

はじめに

Next.js には SSR するための getServerSideProps と SSG/ISR のための getStaticProps というデータフェッチファンクションが用意されています。

一般的な getServerSideProps は、次のようにレンダリングに必要な props を返却することが主な用途です。

export async function getServerSideProps(context) {
  const data = await fetch('...').then((res) => res.json())
  return {
    props: data
  }
}

この使い方は非常に一般的なものであり、Next.js ユーザならほとんどの人が知っていることでしょう。
しかし、getServerSideProps の拡張性に関してはあまり一般的には知られていないようです。

引数である context は Node HTTPServer の reqres を内包しています。
これは、setHeader を通じてレスポンスを書き換えたり、pipe を使ってストリームを取り扱うことが可能であるということを示唆しており、getServerSideProps そのものが HTTPServer のリゾルバとして機能させられるとうことでもあります。

それでは早速、getServerSideProps をリバースプロキシとして稼働させる例を通じて、その拡張性を確認していきましょう。

リバースプロキシ

リバースプロキシのコードはこちらを参考にさせていただきました。
https://qiita.com/suin/items/0ca6d44c7671abdc032b

getServerSideProps をリバースプロキシ化する

前項で紹介したリバースプロキシのコードを getServerSideProps に組み込みます。

// pages/proxy.ts
import { VFC } from 'react'
import { GetServerSideProps, GetServerSidePropsContext } from 'next'
import https, { RequestOptions } from 'https'
import http from 'http'

export const proxy = (
  { req, res }: GetServerSidePropsContext,
  options: RequestOptions,
  isSecure?: boolean
): Promise<void> => {
  return new Promise((resolve) => {
    const serverReq = (isSecure ? https : http)
      .request(options)
      .on('error', () => res.writeHead(502).end())
      .on('timeout', () => res.writeHead(504).end())
      .on('response', (serverRes) => {
        res.writeHead(serverRes.statusCode ?? 200, serverRes.headers)
        serverRes.pipe(res)
      })
      .on('close', resolve)
    req.pipe(serverReq)
  })
}

export const getServerSideProps: GetServerSideProps = async (ctx) => {
  const host = typeof ctx.query.host === 'string' ? ctx.query.host : 'localhost'
  const path = typeof ctx.query.path === 'string' ? ctx.query.path : '/'
  await proxy(
    ctx,
    {
      host,
      path
    },
    true
  )

  return {
    props: {}
  }
}

const Dummy: VFC = () => null
export default Dummy

このように、getServerSidePropsreqhttp(s).request にパイプすることで、リクエスをリバースプロキシに流しています。
さらにレスポンスを getServerSidePropsres にパイプすることで、再度 getServerSideProps 側で処理できるようにするというのが大まかな処理の流れです。

また、pages 配下のファイルは、コンポネントを default export する必要があるため、ダミーの空コンポネントを用意していますが、こちらが処理されることはありません。

ローカルでサーバーを立ち上げ、http://localhost:3000/proxy?host=example.com でアクセスした結果がこちらです。

https://example.comとおなじコンテンツが表示されることが確認できました。
https://example.com

まとめ

タイトル通り、getServerSideProps をリバースプロキシ化することに成功しました。
Node HTTPServer の実装が多少理解できれば、getServerSideProps の拡張は簡単に行なえます。
もし Next.js でトリッキーな実装を行わなければいけなくなった際には、ぜひ挑戦してみてください。

ちなみに

ファイルをPOSTでアップロードしたり、JSONデータをレスポンスとして取り扱ったりするのであれば、元も子もありませんが API Routes を使用すべきです。
https://nextjs.org/docs/api-routes/introduction

どんなケースでこの getServerSideProps の拡張が必要なのかということに関しては、A/BテストをCDNベースで行うためのプラグインで、実際にこのリバースプロキシ化を活用していますので、興味がある方は参照してみてください。
https://github.com/aiji42/next-with-split
かんたんに紹介しておくと、rewrites で A/Bテスト対象のパスをプロキシページに転送し、リバースプロキシによってオリジナル用とチャレンジャ用のページを出し分けるということをしています。


また、つい先日このようなのプラグインも発見したので、こちらも紹介しておきます。
https://github.com/smeijer/next-runtime
https://next-runtime.meijer.ws/getting-started/1-introduction
こちらは、リクエストメソッドに応じた処理を書き分けることを可能にするプラグインです。

GitHubで編集を提案

Discussion