# Gatsby

> ハイドレーション前から静的な HTML で動き、その後は fetch で拡張される Gatsby のフォームと、シークレットキーで送信する Gatsby Function。

Gatsby はビルド時にすべてのページを HTML に描画します。フォームに本物の `action` を付ければ、React がハイドレーションする前や、バンドルの読み込みに失敗した場合でも送信できます。ハイドレーション後は、コンポーネントが `fetch` で送信し、結果をその場に表示します。

## ブラウザーから送信する

### ステップ 1: フォーム ID を追加する

`.env.production`:

```bash
GATSBY_IKIFORM_FORM_ID=frm_xxxxxxxxxxxxxxxx
```

Gatsby がブラウザーのコードに公開するのは `GATSBY_` で始まる変数だけで、ビルド時に埋め込まれます。`gatsby develop` 用に、同じ行を `.env.development` にも追加してください。

### ステップ 2: コンポーネントを追加する

`src/components/contact-form.js`:

```jsx
import * as React from "react";

const endpoint = `https://ikiform.com/f/${process.env.GATSBY_IKIFORM_FORM_ID}`;

export default function ContactForm() {
  const [status, setStatus] = React.useState("idle");
  const [message, setMessage] = React.useState("");

  async function onSubmit(event) {
    event.preventDefault();
    setStatus("sending");
    try {
      const response = await fetch(endpoint, {
        method: "POST",
        headers: { Accept: "application/json" },
        body: new FormData(event.currentTarget),
      });
      const result = await response.json();
      if (result.ok) return setStatus("sent");
      setMessage(result.error.message);
    } catch {
      setMessage("Couldn't reach the server. Try again.");
    }
    setStatus("error");
  }

  if (status === "sent") return <p>Thanks, we got it.</p>;

  return (
    <form action={endpoint} method="post" onSubmit={onSubmit}>
      <label>
        Email
        <input name="email" type="email" autoComplete="email" required />
      </label>
      <label>
        Message
        <textarea name="message" rows={5} />
      </label>
      <input type="hidden" name="_redirect" value="https://example.com/thanks/" />
      <input name="_gotcha" type="text" tabIndex={-1} autoComplete="off" hidden />
      {status === "error" ? <p role="alert">{message}</p> : null}
      <button disabled={status === "sending"}>Send</button>
    </form>
  );
}
```

`src/pages/contact.js` など任意のページから描画し、JavaScript なしの場合のリダイレクト用に `src/pages/thanks.js` を追加します。フィールドエラーや SDK の人間確認を使うには、[React のガイド](https://ikiform.com/ja/docs/guides/react.md#component)のように拡張してください。

## Gatsby Function から送信する

Gatsby Function はシークレットキーを付けて送信を転送できるので、許可ドメインと人間確認をスキップできます。動くのは、Gatsby のアダプターを使った Netlify など、Gatsby Functions に対応したホストだけです。GitHub Pages のような完全な静的ホストでは動きません。`IKIFORM_FORM_ID` と、`submissions:write` スコープを持つシークレットキーを `IKIFORM_SECRET_KEY` として保存します。ブラウザーに届かないよう、`GATSBY_` は付けないでください。

`src/api/contact.js`:

```js
export default async function handler(req, res) {
  if (req.method !== "POST") return res.status(405).end();
  const response = await fetch(`https://ikiform.com/f/${process.env.IKIFORM_FORM_ID}`, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Accept: "application/json",
      Authorization: `Bearer ${process.env.IKIFORM_SECRET_KEY}`,
    },
    body: JSON.stringify(req.body),
  });
  res.status(response.status).json(await response.json());
}
```

Gatsby は JSON とフォームエンコードのボディを `req.body` に解析するので、ページからはどちらの形式でも `/api/contact` に送信できます。その場合、すべての送信がレート制限上は Function の IP アドレスを共有します。[サーバーから送信する](https://ikiform.com/ja/docs/guides/server.md)を参照してください。

## 許可ドメイン

これはブラウザーからの送信に当てはまります。Gatsby Function からの送信はシークレットキーを使うので、チェックをスキップします。

| フォームが動く場所 | 許可ドメインに追加するもの |
| --- | --- |
| `gatsby develop` と `gatsby serve` | `localhost`。`http://localhost:8000` と `http://localhost:9000` に一致します。Ikiform が比較するのはホスト名だけで、ポートは比較しません。 |
| 自分のドメイン | `example.com`（`www.example.com` も含まれます） |
| Netlify | `your-site.netlify.app`。デプロイプレビュー（`deploy-preview-12--your-site.netlify.app`）やブランチデプロイ（`branch--your-site.netlify.app`）はそのサブドメインではないので、テストする間はプレビューの正確なホストを追加してください。 |
| Cloudflare Pages | `your-project.pages.dev`。`abc123.your-project.pages.dev` のようなプレビュー URL はそのサブドメインなので、すでに一致します。 |

- `netlify.app` や `pages.dev` だけを追加しないでください。他の人のサイトも含め、そのホスト上のすべてのサイトが一致してしまいます。

## テストのチェックリスト

1. `gatsby build` を実行し、`public/contact/index.html` を開きます。フォームの `action` が `undefined` ではなく、フォーム ID で終わっているはずです。
2. 許可ドメインに `localhost` を入れて `gatsby serve` を実行し、送信します。お礼のメッセージがフォームに置き換わるはずです。
3. JavaScript をオフにし、再読み込みして送信します。ローカルでは Ikiform のサンクスページが、`https:` のドメインでは `/thanks/` が表示されるはずです。
4. Function を使う場合は、デプロイプレビューで `/api/contact` に送信し、送信データが Ikiform に表示されることを確認します。

## トラブルシューティング

| 症状 | 原因 | 対処 |
| --- | --- | --- |
| `404 not_found` になり、action が `/f/undefined` で終わっている | 変数に `GATSBY_` が付いていない、またはビルド時に設定されていなかった | `GATSBY_IKIFORM_FORM_ID` という名前にして、再ビルドします。 |
| Function が `403 forbidden` を返す | シークレットキーが別のワークスペースのもの、無効化されている、または `submissions:write` がない | そのスコープを持つシークレットキーを作成します。 |
| デプロイ後に `/api/contact` が `404` を返す | ホスティング先が静的ファイルしか配信しない | Gatsby Functions に対応したホストにデプロイするか、ブラウザー版を使います。 |
| 送信データが `origin_mismatch` でスパムフィルターの下に届く | 送信したページが、フォームの許可ドメインに含まれていない | 上の表のホストを追加します。**Reject** を選んでいる場合、同じ送信には代わりに `403 origin_not_allowed` が返ります。 |

## 関連ページ

- [React](https://ikiform.com/ja/docs/guides/react.md): 送信中、エラー、成功の状態。
- [サーバーから送信する](https://ikiform.com/ja/docs/guides/server.md): シークレット API キーで送信します。
- [許可ドメイン](https://ikiform.com/ja/docs/spam.md#allowed-domains): オリジンの照合方法と、一致しない場合の動作。
- [エラー](https://ikiform.com/ja/docs/errors.md): すべてのエラーコードと、その原因と対処。
