Murasaki
ガイド

Server Actions

Node 上で実行され、React からは useAction で呼び出される 'use server' 関数。

React 19 スタイルのサーバーアクションです — useActionState と同じ形です。'use server' 関数は、実際にはブラウザではなく Node 上で実行されます。

アクションを定義する

src/actions.ts
'use server'
import { defineAction } from 'murasaki'
import type { ActionState } from 'murasaki'

export const greet = defineAction(
  async (_prev: ActionState<string>, formData: FormData): Promise<ActionState<string>> => {
    const name = formData.get('name')
    return { data: `Hello, ${name}!`, error: null, isPending: false }
  },
)

defineAction は型付きのパススルーです — 'use server' のセマンティクスが TypeScript を通じて伝わるようにするためだけに存在します。実際のコード分割は、そのディ レクティブを検出する Vite プラグインの中で行われます。

ページから呼び出す

src/app/page.tsx
import { useAction } from 'murasaki'
import { greet } from '../actions'

export default function Home() {
  const [state, run, isPending] = useAction(greet, {
    data: null,
    error: null,
    isPending: false,
  })

  return (
    <form action={run}>
      <input name="name" />
      <button disabled={isPending}>Greet</button>
      {state.data && <p>{state.data}</p>}
    </form>
  )
}

useAction は React 19 の useActionState を直接ラップしているため、[state, run, isPending] は Next.js ですでにおなじみの形そのままです。<form> の外(例えばボタンの onClick など)からアクションを呼び出すには、直接呼び出します — callAction(fn, ...args) はそのための薄いパススルーです。

実行の仕組み

Vite プラグインは 'use server' モジュールを2つに分割します。クライアントバンドルに は型付きの fetch スタブ(fetch('/__murasaki/action/…'))が入り、関数の本体はサー バー側に残ります。

  • 開発時 — Vite ミドルウェアがスタブのリクエストを受け取り、ssrLoadModule 経由で 実際の関数を呼び出します。
  • 本番時murasaki bundle が自己完結型のアクションレジストリ (dist/server)をコンパイルし、(ネイティブランチャーによって起動される)バンドル 済みの Node 子サーバーが、同じ /__murasaki/action/… リクエストをそれに対して処理し ます。

いずれの場合も、アクション本体そのものがクライアントに配信されることはなく、スタブだけ が配信されます。

'use server' ファイルは src/ 配下のどこにでも置けます(src/actions.ts が一般的 な慣習です)— src/app/src/api/ に紐づく必要はありません。

次へ

GitHub でこのページを改善

On this page