Next.jsのServer Actionsをもっと便利に安全に使うnext-safe-actionを紹介したい

## 前置き
突然ですが、Next.jsのServer Actionsって便利ですよね？皆さん使っていますか？  
ただ、もっとこんなことができたらいいのになぁって思う瞬間があるかもしれません。例えば、

- エラーハンドリングを共通化したい
- 認可処理を共通化したい
- 上記をやりつつ型安全を維持したい

と思ったことはありませんか？

そんな時は、`next-safe-action`を使用すれば全て解決するかもしれません。

## next-safe-actionとは
`next-safe-action`は、Next.jsのServer Actionsをラップし、安全に扱えるようにするライブラリです。

公式ドキュメント：[next-safe-action](https://next-safe-action.dev/docs/getting-started)

主な特徴は以下の通りです。

- 型安全なServer Actionsの作成
- 一元的なエラーハンドリング
- Middlewareによる共通処理の適用

## 使い方
### インストール
まずは、`next-safe-action`をインストールします。
ついでに、client側のformライブラリ(react-hook-form）とvalidation libraryをインストールします。

```sh
npm install next-safe-action zod react-hook-form @hookform/resolvers
```

### createSafeActionの使い方
`createSafeAction`を使用すると、型安全なServer Actionを簡単に作成できます。

```typescript
'use server';

import { createSafeAction } from 'next-safe-action';
import { z } from 'zod';

const action = createSafeActionClient({
  // エラーハンドリング処理
  handleServerError(e, utils) {
    // You can access these properties inside the `utils` object.
    const { clientInput, bindArgsClientInputs, metadata, ctx } = utils;

    // Log to console.
    console.error("Action error:", e.message);

    // 共通的なエラー処理
    return "エラーが発生しました";
  },
});

export const sampleSchema = z.object({
  name: z.string(),
});

export const sampleAction = action
  .schema(sampleSchema)
  .action(async ({ input }) => {
    return `Hello ${input.name}`
  });

export default action;
```

このように`createSafeAction`を使うことで、共通的なactionを定義することができます。
ここでは、「共通的なエラーハンドリング」が実現できます。
さらに、各actionではshcemaを指定することで、簡単に型を指定することができます。

### Middlewareの活用
Middlewareを利用することで、認可処理やロギングを共通化できます。

```typescript
import { createSafeAction } from 'next-safe-action';
import { z } from 'zod';

const action = createSafeActionClient({
  // エラーハンドリング処理
  handleServerError(e, utils) {
    // You can access these properties inside the `utils` object.
    const { clientInput, bindArgsClientInputs, metadata, ctx } = utils;

    // Log to console.
    console.error("Action error:", e.message);

    // 共通的なエラー処理
    return "エラーが発生しました";
  },
}).use(async ({ next }) => {
    const session = cookies().get("session")?.value;

    if (!session) {
      throw new Error("Session not found!");
    }

    const userId = await getUserIdFromSessionId(session);

    if (!userId) {
      throw new Error("Session is not valid!");
    }

    // Return the next middleware with `userId` value in the context
    return next({ ctx: { userId } });
  });;
```
actionの定義の後に```.use```と繋げることで共通的なロギング処理や認可処理も定義することが可能です！

## Client側の実装
Client側では、`react-hook-form`と`zod`を組み合わせてフォームを作成し、`next-safe-action`を活用できます。

```typescript
'use client';

import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { useAction } from "next-safe-action/hooks";
import { z } from 'zod';
import { sampleAction, sampleSchema } from '../actions';
import { useState } from 'react';


type FormData = z.infer<typeof sampleSchema>;

export default function Form() {
  // useActionで簡単にaction処理を呼び出せます！
  const { execute } = useAction(sampleAction);

  const { control, handleSubmit } = useForm<FormData>({
    defaultValues: {
      name: "John",
    },
    resolver: zodResolver(sampleSchema),
  });

  return (
    <form onSubmit={handleSubmit(execute)}>
      <div>
        <label>名前:</label>
        <input {...register('name')} />
      </div>
      <button type="submit">送信</button>
      {response && <p>サーバーからの応答: {response}</p>}
    </form>
  );
}
```

このフォームでは、

- `react-hook-form` でフォーム管理
- `zod` で入力バリデーション
- `next-safe-action` で型安全なServer Actionの呼び出し

を実現しています。


clinet側では```useAction```が超絶便利！
https://next-safe-action.dev/docs/execute-actions/hooks/useaction
処理だけではなく、statusなども取得できるし、callback系の処理を引数に指定できるので成功時の処理などを簡単にわかりやすく指定できます！




## まとめ
Next.jsのServer Actionsをより安全に使うためには、`next-safe-action`が非常に便利です。

- `createSafeAction`で型安全・エラーハンドリングを強化
- `middleware`で認可処理を共通化
- `react-hook-form`と`zod`でClient側のフォームを型安全に実装

この記事では伝えていない機能もたくさんあるので是非みなさん使ってみてください！