💭

Next.jsのMiddlewareを使ってメンテナンスモードを表示したメモ

2022/06/15に公開

Next.js & Vercel は便利ですが、2022年6月時点でVercelはメンテナンスモードを提供していません。なので、Next.jsで自分で実装します。

メンテナンスモードの実装方法は検索すればたくさん出てきますが、今回は Next.js v12 から追加された Middleware を使用して実装してみます。

今回の実装条件は以下です。

  1. 環境変数の値によって、メンテナンスモードを切り分け
  2. メンテナンスモードの場合はメンテナンスページを表示
  3. その際はステータスコード503を返す

とり急ぎ実装したやり方をメモしておきますが、もっといいやり方がありそうな気がします。

手順

1. 環境変数を用意

process.env.NEXT_PUBLIC_MAINTENANCE_MODE === 'true'

2. メンテナンスモード用のエラーページを作成する。

別にエラーページにしなくても良いかもしれないが、503で返すので カスタムエラーページ を作成してみた。

Maintenance.tsx
import styled from '@emotion/styled'

export const Maintenance = () => {
  return (
    <Container>
      <Heading>Under maintenance</Heading>
      <Paragraph>
        This site is under maintenance.
      </Paragraph>
    </Container>
  )
}

Emotionで実装。CSS等は割愛。

pages/_error.tsx
import { NextPage, NextPageContext } from 'next'
import Error from 'next/error'
import { Maintenance } from './Maintenance'

const ErrorPage: NextPage<{ statusCode: number }> = ({ statusCode }) => {
  if (statusCode === 503) {
    return <Maintenance />
  }

  return <Error statusCode={statusCode} />
}

// Ref: https://nextjs.org/docs/advanced-features/custom-error-page#caveats
ErrorPage.getInitialProps = ({ res, err }: NextPageContext) => {
  const statusCode = res?.statusCode || err?.statusCode || 404
  return { statusCode }
}

export default ErrorPage

カスタムエラーページ のドキュメントを参考に、ステータスコードで場合分け。
(いいかげん getInitialProps はやめたいのだが...)

3. _app.tsx で表示

どんな書き方でもいいが、ErrorPageにステータスコード503を渡して、環境変数で出し分けする

pages/_app.tsx
const MyApp = () => {
  ...(省略)
}

const MaintenanceView = () => (
  <ErrorPage statusCode={503} />
)

export default process.env.NEXT_PUBLIC_MAINTENANCE_MODE === 'true'
  ? MaintenanceView
  : MyApp

4. Middlewareでレスポンスのステータスコードを503とする

3まででもメンテナンスモードの表示はできるが、ステータスコードが200のままになってしまう。
どうするか悩んだあげく、Middlewareで上書きすることにした。

_middleware.tsx
import { NextResponse } from 'next/server'

export const middleware = () => {
  const response = NextResponse.next()

  if (process.env.NEXT_PUBLIC_MAINTENANCE_MODE === 'true') {
    // Overwrite only status code 503
    return new NextResponse(response.body, {
      headers: response.headers,
      statusText: response.statusText,
      url: response.url,
      status: 503,
    })
  }

  return response
}

ステータスだけを変更する方法が↑でいいのかはわからないが、とりあえずこれで。

5. チェック

コンポーネントが表示されているか、そしてステータスコードが503になっているかをチェックする。
色々やり方はあるが例えば「cURLでHTTPステータスコードだけを取得する」 でステータスコードを見てみる。

$ curl -LI http://localhost:4200/ -o /dev/null -w '%{http_code}\n' -s
503

OK.

所感

メンテナンスモードのロジックを _middleware.tsx_app.tsx で二回書いていて微妙だし、正直もっといいやり方があると思う。

コメントお待ちしております。

Discussion