⚠️
Next.js 14 + firebase-adminでserver actionsを実行すると、webpack errorになる
概要
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そうです。
参考
Discussion