NetlifyでNext.jsのSSR(serverless mode)を実行する

Next.js Advent Calendar 2020の5日目記事です。

Next.jsで作ったアプリケーションのデプロイといえばVercelですが、Netlifyもなかなか頑張っています。

公式にリリースされたプラグインを使うことで、APIやserverless Modeでの実行などが可能ですので、軽く紹介します。

npm install -D next-on-netlify

NetlifyチームがリリースしているNetlify向けプラグインです。これを使うことで、Netlify上にデプロイしたNext.jsでもAPIやSSRが利用できます。

Install

npmにて公開されていますので、npm installで追加します。

$ npm install -D next-on-netlify

postbuildに指定

このプラグインはpostbuildでの実行が推奨されていますので、package.jsonに指定します。

{
  "name": "my-nextjs-app", 
  "scripts": {
     "dev": "next",
     "build": "next build",
     "postbuild": "next-on-netlify"

target: “serverless”に変更

next.config.jsを更新して、ビルドのターゲットをserverlessに変更しておきましょう。

module.exports = {
  target: "serverless"
}

netlify.tomlにビルド設定を定義

最後にNetlify側のビルド設定を定義します。

[build]
   command   = "npm run build"
   functions = "out_functions"
   publish   = "out_publish"

あとはGitで変更をコミットし、pushしてNetlifyのビルドを実行するだけです。

getStaticPathsのfallbackにも対応

このプラグインを入れると、NetlifyにホストしたNext.jsでもfallbackが利用できます。

下のコードは、WP APIから記事を取得して詳細ページを表示するサンプルpages/[id].tsxです。

import Axios from 'axios'
import purify from "dompurify";
import Link from 'next/link'
import { useRouter } from 'next/router'
import { GetStaticPaths, GetStaticProps, NextPage } from "next";

export const Page: NextPage<{
    post: {
        title: {
            rendered: string;
        }
        content: {
            rendered: string;
        }
    }
}> = (props) => {
    const router = useRouter()
 
    if (router.isFallback) {
      return <div>Loading...</div>
    }
    return (
        <main>
            <h1 dangerouslySetInnerHTML={{
                __html: purify.sanitize(props.post.title.rendered)
            }} />
                <div dangerouslySetInnerHTML={{
                    __html: purify.sanitize(props.post.content.rendered)
                }} />
            </main>
    )
}

export const getStaticPaths: GetStaticPaths = async () => {
    const {data: posts} = await Axios.get('https://example.com/wp-json/wp/v2/posts?per_page=10')

    return {
        paths: posts.map(post => ({
            params: {
                id: `${post.id}`,
            }
        })),
        fallback: true
    }
}

export const getStaticProps: GetStaticProps = async ({params}) => {
    const id = Array.isArray(params.id) ? params.id[0] : params.id
    const {data: post} = await Axios.get('https://example.com/wp-json/wp/v2/posts/' + id)
    return {
        props: {
            post
        }
    }
}

getStaticPathsではper_page=10と最新10記事のみ生成させています。SSG(next export)の場合は、ここで生成していない記事は、HTMLが生成されないために404となります。ですがnext-on-netlifyを利用した場合は、Netlifyでもfallback: trueを利用できますので、「なければSSRしてページ生成(= 都度SSG)」が可能になります。

next-on-netlifyはNetlify Functionを利用する

next-on-netlifyを利用してデプロイした場合、自動的にNetlify Functionがデプロイされます。

もちろんNetlify Functionには無料枠の制限がありますので、それを超えると実行できなくなります。

月間12.5万リクエスト、100実行時間までは無料です。fallbackで静的化されるであろうことを考えると「12.5万PVまでしか無料で使えない」というわけではなさそうですが、PVが多いサイトでは課金もしくはVercel or 自前Node.jsサーバーへの移行を検討することになるでしょう。

ちなみに課金はこんな感じです。1段階あげるだけで月25ドルいかれますが、利用枠も2000万リクエスト/1000実行時間まで一気に増えてくれます。

で、ISRは?

「段階的にサポート頑張ってます」という状況みたいです。現時点では。

普通にrevalidategetStaticPropsに設定すると動いているように見える挙動をしてくれます。ただ、ISRについてのIssueがOpenなままなので、もしかすると何かしら未対応のものがあるかもしれません。

あと、Netlify Functionの実行数がどんどん増えそうなので、お財布が心配な方はあまりおすすめしないかもです。

Netlify or Vercel?

どちらにしてもいつまでも無料で使えるとは思わないようにしておくのが一番ストレスないかなとは思います。

Netlify FormやAccess Control・プラグインなどの機能をよく使っているのであればnext-on-netlifyで頑張りたくなりますし、そこまでNetlifyに思い入れもビジネス的な依存もないなら早めにVercelを検討してもよいかなという風に個人的には思っています。

Comment