NewtとSvelteKitを利用して、問い合わせフォームを作成する

最終更新日:

Table of contents

  1. 記事内で使用している主なソフトウェアのバージョン
  2. 前提条件
  3. 概要
  4. 1. Form Appを作成する
  5. 1-1. Form Appの追加
  6. 1-2. フォームの作成
  7. 2. SvelteKitでシンプルな問い合わせフォームを作成する
  8. 2-1. HTMLフォームの追加
  9. 2-2. 自動返信メールの設定
  10. 2-3. 受信通知メールの設定
  11. 2-4. 挙動の確認
  12. 3. バリデーションを追加する
  13. 3-1. input要素の属性を利用したバリデーション
  14. 3-2. JavaScriptを利用したバリデーション
  15. 4. スパム対策を実装する
  16. 4-1. サイトキーとシークレットキーの取得
  17. 4-2. シークレットキーの登録
  18. 4-3. @types/grecaptchaのインストール
  19. 4-4. SvelteKitでの設定
  20. 5. リダイレクトをカスタマイズする
  21. 5-1. 送信成功ページ・送信失敗ページを用意する
  22. 5-2. リダイレクトを設定する

このチュートリアルでは、Newtの Form AppSvelteKit を利用して、問い合わせフォームを作成する手順を紹介します。

記事内で使用している主なソフトウェアのバージョン

  • SvelteKit(@sveltejs/kit): 1.10.0
  • Felte(felte): 1.2.7

前提条件

  • SvelteKitのプロジェクトを作成済みであること

SvelteKitのセットアップについて知りたい場合は、以下のドキュメントをご確認ください。

概要

SvelteKitで問い合わせフォームを作成し、Newtで問い合わせデータを確認できるようにします。
はじめに、自動返信・受信通知のついたシンプルなフォームを作成し、その後でバリデーション・スパム対策・リダイレクトの処理を実装しながら、カスタマイズの方法を学習していきます。
※ ここではスタイルについては扱いません。スタイルについて確認したい方は、Next.jsのコードとなりますが、問い合わせ用のテンプレート newt-starter-nextjs-contact を用意しているので、そちらのコードをご確認ください。

1. Form Appを作成する

はじめに、NewtでForm Appを作成します。

1-1. Form Appの追加

「Appを追加」をクリックして「タイプを選択して追加」を選択します。
form-tutorial1.jpg

「Form App」を選択して「追加」をクリックします。
form-tutorial2.jpg

App名・App UID・Appアイコンを設定して「追加」をクリックします。
form-tutorial3.jpg

これで「Contact」という名前のForm Appが追加されました。
form-tutorial4.jpg

1-2. フォームの作成

次にフォームを作成します。「フォームを作成」ボタンをクリックし、名前を付けてフォームを作成します。
form-tutorial5.jpg

作成後に表示されるエンドポイントは、2-1で利用します。
form-tutorial6.jpg

2. SvelteKitでシンプルな問い合わせフォームを作成する

続いて、SvelteKitでシンプルな問い合わせフォームを作成しましょう。
ここでは /contact というパスにアクセスした時に、問い合わせフォームを表示するようにします。

2-1. HTMLフォームの追加

src/routes/contact/+page.svelte を作成し、以下のように記述します。
formaction 属性に設定している https://xxxxxx.form.newt.so/v1/xxxxxx の値については、1-2で確認したエンドポイントの値に置き換えてください。

<!-- src/routes/contact/+page.svelte -->

<svelte:head>
  <title>Newt・SvelteKitフォーム</title>
  <meta name="description" content="NewtとSvelteKitを利用した問い合わせフォームです" />
</svelte:head>

<div>
  <h1>Contact us</h1>
  <form action="https://xxxxxx.form.newt.so/v1/xxxxxx" method="post">
    <label for="name">Name</label>
    <input id="name" name="name" />
    <label for="email">Email</label>
    <input id="email" name="email" type="email" />
    <label for="message">Message</label>
    <textarea id="message" name="message" />
    <button type="submit">Submit</button>
  </form>
</div>

各属性について説明します。

label要素のfor属性と、input要素・textarea要素のid属性
label要素for 属性は、input要素textarea要素id 属性と一致するように設定します。

input要素・textarea要素のname属性
input要素・textarea要素のnameで設定された値をもとに、投稿データのスキーマが決まります。例えば上記の例では、nameemailmessage フィールドを持ったスキーマが作成されます。
詳細については、フィールドの仕様 のドキュメントをご確認ください。

input要素のtype属性
お好みの入力形式に応じて、inputの型 を選択してください。デフォルトでは text となります。

これで http://localhost:5173/contact にアクセスすると、以下のような問い合わせフォームが作成できました。
※ スタイルについてはお好みで設定してください
sveltekit-form1.png

2-2. 自動返信メールの設定

自動返信メール の設定をします。自動返信メールを設定することで、投稿を送信したエンドユーザーに対して、設定した内容のメールを自動で送信できます。

※ Newtは自動返信メールの送信先メールアドレスを検出するために email フィールドを使います。input要素のname属性に email を指定し、エンドユーザーのメールアドレスが入力されるようにしてください。

「App設定」から該当のフォームをクリックし、「メール設定」を選択します。
「自動返信メールを有効にする」にチェックを入れ、件名と本文を入力し、「保存」をクリックします。

form-tutorial7.jpg

自動返信メールでは {submission.name} のように記述することで、メールの件名や本文に、投稿データの内容を埋め込めます。
2-1で、input要素・textarea要素のname属性に nameemailmessage を指定したので、ここでは {submission.name}{submission.email}{submission.message} を利用できます。

2-3. 受信通知メールの設定

受信通知メール の設定をします。受信通知メールを設定することで、エンドユーザーから新しいメッセージを受信した際に、メールで通知を受け取れます。

「App設定」から該当のフォームをクリックし、「メール設定」を選択します。
「受信通知メールを有効にする」にチェックを入れ、通知を受け取るメールアドレス・件名・本文を設定し、「保存」をクリックします。

form-tutorial8.jpg

受信通知メールでも、自動返信メールと同様に、投稿データの内容を埋め込めます。

2-4. 挙動の確認

これで、シンプルな問い合わせフォームを作成できました。
実際に動かして、挙動を確認してみましょう。

まず、開発サーバーを立ち上げて、問い合わせフォームを表示します。ここでは http://localhost:5173/contact にアクセスします。
適当な内容を入力して送信すればよいですが、設定したEmail(name属性に email を指定した入力値)に自動返信メールが届くので、ご自身のメールアドレスを入力するよう気をつけてください。
sveltekit-form2.png

送信が成功すると、以下の画面が表示されます。
form-tutorial9.png

管理画面では、以下のようにデータが確認できます。
form-tutorial10.jpg

次に自動返信メールを確認します。以下のようなメールが飛んでいれば成功です。
form-tutorial11.png

最後に受信通知メールを確認します。以下のようなメールが飛んでいれば成功です。
form-tutorial12.png

これでシンプルなフォームの作成・自動返信メールの設定・受信通知メールの設定ができました。ここからはフォームのカスタマイズをしながら、より高度なフォームの作成方法を学んでいきましょう。

3. バリデーションを追加する

クライアントサイドのバリデーションについて、以下の順で紹介します。

  • input要素の属性を利用したバリデーション
  • JavaScriptを利用したバリデーション(Felteを利用したバリデーション)

3-1. input要素の属性を利用したバリデーション

HTML5のフォームバリデーションを利用することで、JavaScriptなしでバリデーションを実行できます。よく使われるものについては HTML5のフォームバリデーション をご確認ください。

例えば、name を必須にしたい場合は、以下のように required を指定します。

<input id="name" name="name" required />

もし name を指定せず、送信を実行した場合は、以下のように表示されます。
sveltekit-form3.png

3-2. JavaScriptを利用したバリデーション

HTMLネイティブのフォームバリデーションでは足りない場合、JavaScriptを利用することで、より柔軟なバリデーションを実行できます。
ここでは、バリデーションのタイミングを変え、さらにエラーメッセージをカスタマイズしてみましょう。

JavaScriptを利用してフォームを送信する場合(Acceptヘッダーでapplication/jsonを指定してレスポンスをJSONデータとして受け取る場合)、ご自身で送信後の表示を制御していただくことになります。
デフォルトのThanksページ・Errorページへのリダイレクトや カスタムリダイレクト は行われなくなりますので、ご注意ください。

ここでは Felte を利用してバリデーションを行います。まずは felte をインストールしましょう。

npm install felte
# or
yarn add felte

続いて、以下のように src/routes/contact/+page.svelte を修正します。
fetch の リソースとして設定されている https://xxxxxx.form.newt.so/v1/xxxxxx の値については、1-2で確認したエンドポイントの値に置き換えてください。

<!-- src/routes/contact/+page.svelte -->

<script lang="ts">
  import { createForm } from 'felte'

  type FormValues = {
    name: string
    email: string
    message: string
  }

  const { form, errors } = createForm<FormValues>({
    validate: (values) => {
      const errors: Record<string, string> = {}
      if (!values.name) {
        errors.name = 'Name is required'
      }
      return errors
    },
    onSubmit: async (values) => {
      const formData = new FormData()
      Object.entries(values).forEach(([key, value]) => {
        formData.append(key, value)
      })

      await fetch('https://xxxxxx.form.newt.so/v1/xxxxxx', {
        method: 'POST',
        body: formData,
        headers: {
          Accept: 'application/json'
        }
      })
    }
  })
</script>

<svelte:head>
  <title>Newt・SvelteKitフォーム</title>
  <meta name="description" content="NewtとSvelteKitを利用した問い合わせフォームです" />
</svelte:head>

<div>
  <h1>Contact us</h1>
  <form use:form>
    <label for="name">Name*</label>
    <input id="name" name="name" aria-describedby="error-name-required" />
    {#if $errors.name}
      <span id="error-name-required" aria-live="assertive">
        {$errors.name}
      </span>
    {/if}
    <label for="email">Email</label>
    <input id="email" name="email" type="email" />
    <label for="message">Message</label>
    <textarea id="message" name="message" />
    <button type="submit">Submit</button>
  </form>
</div>

以下のことを行っています。

  • 入力値の型として、FormValues を定義し、createForm にジェネリクスとして渡します
  • createFormvalidate でバリデーション関数を定義できます。ここでは、name がない場合に Name is required というエラーメッセージを設定しています
    ※ 詳細はFelteの Validation のドキュメントをご確認ください
  • 入力されたフォームのデータは onSubmit 関数で values として利用できます
    ※ 詳細はFelteの Submitting のドキュメントをご確認ください
  • FormData を利用して、送信するデータ formData を作成します
  • フェッチ API を利用して、1-2で確認したエンドポイントにデータを送ります。この時、Acceptヘッダーに 'application/json' を指定します

また、以下の部分で validate で設定したエラーメッセージを出しています。(ここでは name がない場合に Name is required というエラーメッセージを出しています)
他にも様々なバリデーションが設定できるので、Felteの ValidationValidators のドキュメントをご確認ください。

<label for="name">Name*</label>
<input id="name" name="name" aria-describedby="error-name-required" />
{#if $errors.name}
  <span id="error-name-required" aria-live="assertive">
    {$errors.name}
  </span>
{/if}

以下のことを確認します。

  • 入力変更があったタイミングでバリデーションが実行されること
  • name が入力されていない場合に Name is required というエラーメッセージが出ること

sveltekit-form4.png

4. スパム対策を実装する

続いて、スパム対策 として Google reCAPTCHA v3 の設置方法について説明します。

4-1. サイトキーとシークレットキーの取得

Google reCAPTHAのコンソール にアクセスし、新しいサイトを登録します。

reCAPTCHAタイプは reCAPTCHA v3 を選択します。
ここではローカル環境でのテストを行うので、ドメインには localhost を追加します。本番環境で設定したい場合は、reCAPTCHAを導入したいサイトのドメインを設定してください。
form-tutorial13.jpg

全ての項目を入力して「送信」をクリックすると、サイトキーとシークレットキーが表示されます。
form-tutorial14.jpg

4-2. シークレットキーの登録

Newtの管理画面にアクセスし、「App設定」を開きます。該当のフォームをクリックし、「フォーム設定」を選択します。
「スパム対策」のセクションより、「Google reCAPTCHA v3を有効にする」にチェックを入れ、4-1で取得したシークレットキーを貼り付けて保存します。
spam-filtering1.png

4-3. @types/grecaptchaのインストール

後述の処理で必要になるため、@types/grecaptcha をインストールしておきます。

npm install --save-dev @types/grecaptcha
# or
yarn add -D @types/grecaptcha

4-4. SvelteKitでの設定

最後にSvelteKitでの設定を行います。
3-2で作成した src/routes/contact/+page.svelte を以下のように修正します。

また、以下の箇所については、正しい値に置き換えるよう注意してください。

  • grecaptcha.execute で指定してる reCAPTCHA_site_key には、4-1で取得したサイトキーを入力してください
  • sendRequest メソッドで送信している https://xxxxxx.form.newt.so/v1/xxxxxx は、1-2で確認したエンドポイントの値に置き換えてください
  • https://www.google.com/recaptcha/api.js?render=reCAPTCHA_site_key で指定してる reCAPTCHA_site_key には、4-1で取得したサイトキーを入力してください
<script lang="ts">
  import { createForm } from 'felte'

  type FormValues = {
    name: string
    email: string
    message: string
+   googleReCaptchaToken: string
  }

  const { form, errors } = createForm<FormValues>({
    validate: (values) => {
      const errors: Record<string, string> = {}
      if (!values.name) {
        errors.name = 'Name is required'
      }
      return errors
    },
    onSubmit: async (values) => {
-     const formData = new FormData()
-     Object.entries(values).forEach(([key, value]) => {
-       formData.append(key, value)
-     })
-
-     await fetch('https://xxxxxx.form.newt.so/v1/xxxxxx', {
-       method: 'POST',
-       body: formData,
-       headers: {
-         Accept: 'application/json'
-       }
-     })
+     grecaptcha.ready(() => {
+       grecaptcha
+         .execute('reCAPTCHA_site_key', { action: 'submit' })
+         .then(async (token) => {
+           values.googleReCaptchaToken = token
+
+           const formData = new FormData()
+           Object.entries(values).forEach(([key, value]) => {
+             formData.append(key, value)
+           })
+
+           await sendRequest(formData)
+         })
+     })
+
+     const sendRequest = async (formData: FormData) => {
+       return await fetch('https://xxxxxx.form.newt.so/v1/xxxxxx', {
+         method: 'POST',
+         body: formData,
+         headers: {
+           Accept: 'application/json'
+         }
+       })
+     }
    }
  })
</script>

<svelte:head>
  <title>Newt・SvelteKitフォーム</title>
  <meta name="description" content="NewtとSvelteKitを利用した問い合わせフォームです" />
+ <script
+   src="https://www.google.com/recaptcha/api.js?render=reCAPTCHA_site_key&hl=ja"
+   async
+   defer
+ ></script>
</svelte:head>

(省略)

以下のことを行っています。

  • googleReCaptchaToken というフィールド名でトークンを送信します(他の名前で送信した場合、正しく動作しないので注意してください)
  • サイトキーを使用して、reCAPTCHAのJavaScript APIを読み込みます
  • reCAPTCHAのJavaScript APIの読み込みでは hl クエリに ja を設定しているため、問い合わせフォームの右下に表示されるreCAPTCHAのバッジが日本語になります。他の言語で設定したい方は、Googleの Language Codes のドキュメントを参考に設定してください

これでGoogle reCAPTCHA v3によるスパム対策が実装できました。

form-tutorial15.jpg

もし、バッジを非表示にしたい場合、.grecaptcha-badge { visibility: hidden; } を適用すると非表示になりますが、この操作はGoogle公式のガイドに従った場合のみ許可されます。
詳細は、Googleの reCAPTCHA バッジを非表示にします。どうすればよいですか? のドキュメントをご確認ください。

5. リダイレクトをカスタマイズする

最後にフォーム送信後のリダイレクトをカスタマイズします。
送信が成功した場合のページと、失敗した場合のページを用意して、送信結果に応じてリダイレクトしてみましょう。

5-1. 送信成功ページ・送信失敗ページを用意する

送信成功ページとして、以下を用意します。
src/routes/thanks/+page.svelte を作成します。

<svelte:head>
  <title>Thank you</title>
  <meta name="description" content="問い合わせの送信が成功しました" />
</svelte:head>

<div class="Result">
  <h1>Thank you!</h1>
  <div>
    <a href="/contact">Back to Previous Page</a>
  </div>
</div>

送信失敗ページとして、以下を用意します。
src/routes/error/+page.svelte を作成します。

<svelte:head>
  <title>Error</title>
  <meta name="description" content="問い合わせの送信が失敗しました" />
</svelte:head>

<div class="Result">
  <h1>Error!</h1>
  <div>
    <a href="/contact">Back to Previous Page</a>
  </div>
</div>

5-2. リダイレクトを設定する

src/routes/contact/+page.svelte を以下のように修正します。

+ import { goto } from '$app/navigation'
  import { createForm } from 'felte'

(省略)

    onSubmit: async (values) => {
      grecaptcha.ready(() => {
        grecaptcha
          .execute('reCAPTCHA_site_key', { action: 'submit' })
          .then(async (token) => {
            values.googleReCaptchaToken = token

            const formData = new FormData()
            Object.entries(values).forEach(([key, value]) => {
              formData.append(key, value)
            })

-           await sendRequest(formData)
+           try {
+             const response = await sendRequest(formData)
+             if (response.ok) {
+               goto('/thanks')
+             } else {
+               goto('/error')
+             }
+           } catch (err) {
+             goto('/error')
+           }
          })
      })

(省略)

送信が成功した場合(response.ok がtrueの場合)は goto を利用して /thanks にリダイレクトし、送信成功ページを表示しています。
送信が失敗した場合(response.ok がfalseの場合、またはエラーが発生した場合)は /error にリダイレクトし、送信失敗ページを表示しています。

これでリダイレクトをカスタマイズすることができました。

以上で、すべてのステップが終了となります。

Form Appでは、他にもさまざまな機能があるので、興味のある方はぜひ以下のドキュメントもご覧ください。

NewtMade in Newt