⚠️

Next.js 14 + firebase-adminでserver actionsを実行すると、webpack errorになる

2023/11/06に公開

概要

Next.js14のserver actionsで、firestoreに書き込みをしようとしたところ、Client ComponentにServer Actionをインポートする方法ではwebpackのエラーが発生し、実行できませんでした。
原因は不明ですが、代替のProps経由で渡す方法で処理可能です。

  • next (v14.0.1)
  • firebase-admin (v11.11.0)

(失敗パターン)Client ComponentにServer Actionをインポートする方法

こちらを頼りに、firestoreにデータを保存するserver actionを作成してみます。

Clientのコンポーネントは、ドキュメントほぼと同じで、サンプル用にinputだけ追加してます。

src/app/client-component.tsx
"use client"

import { myAction } from "./actions"

export default function ClientComponent() {
  return (
    <form action={myAction}>
      <label htmlFor="name">Name</label>
      <input
        type="text"
        name="name"
        value="foo"
        id="name"
      />
      <button type="submit">Submit</button>
    </form>
  )
}

myAction はこのように実装

src/app/actions.ts
"use server"

import { firestore } from "@/server/firebase"

export async function myAction(formData: FormData) {
  console.log("formData", formData.get("name"))
  await firestore.collection("sample").add({ name: formData.get("name") })
}

こちらファイルでfirestore clientを初期化します。

src/server/firebase.ts
import "server-only"
import { getApps, applicationDefault, initializeApp } from "firebase-admin/app"
import { getFirestore } from "firebase-admin/firestore"

const a =
  getApps()[0] ??
  initializeApp({
    projectId: process.env.FIREBASE_PROJECT_ID,
    credential: applicationDefault(),
  })

export const firestore = getFirestore(a)

最後に、ClientComponent を適当なpageに配置します。

src/app/page.tsx
import ClientComponent from "@/app/client-component"

export default async function Home() {
  return (
    <main>
      <ClientComponent />
    </main>
  )
}

Submitを押すと、以下のエラーが発生します。

 ⨯ SyntaxError: Unexpected identifier
    at (action-browser)/./src/server/firebase.ts (/.../page.js:598:1)
    at __webpack_require__ (/.../.next/server/webpack-runtime.js:33:43)
    at eval (./src/app/actions.ts:8:74)
    at (action-browser)/./src/app/actions.ts (/.../.next/server/app/(public)/page.js:498:1)
    at Function.__webpack_require__ (/.../.next/server/webpack-runtime.js:33:43)

(成功パターン)Props経由で渡す方法

myAction をprop経由で Client Componentに渡すように修正します。(ドキュメントはこちら)

Server Componentである、page.tsx でServer Actionをインポートして、Client Component にpropsとして渡します。

src/app/page.tsx
import { myAction } from "@/app/actions"  // Server Componentでインポート
import ClientComponent from "@/app/client-component"

export default async function Home() {
  return (
    <main>
      <ClientComponent action={myAction} />
    </main>
  )
}

Client Component では、propsで受け取ったactionを実行するようにします。

src/app/client-component.tsx
"use client"

export default function ClientComponent(props: {
  action: (formData: FormData) => Promise<void>
}) {
  const { action } = props
  return (
    <form action={action}>
      <label htmlFor="name">Name</label>
      <input type="text" name="name" value="foo" id="name" />
      <button type="submit">Submit</button>
    </form>
  )
}

他のファイルは変更なしです。

これで、Submitボタンを押すと、エラーなく処理が完了し、firestoreにデータが保存されてます。サーバのほうにconsole.log の出力が出ており、サーバサイドでの実行もOKそうです。

参考

https://github.com/vercel/next.js/discussions/57535

株式会社Poksha

Discussion