2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

🔧 Nuxt.js × Firebase で作る!無料で実装できるお問い合わせフォーム&スパム対策 🚀

Posted at

以下の手順では、Nuxt.js と Firebase(Firestore や Cloud Functions を併用する場合もあり)のみを利用してお問い合わせフォームを作成する方法と、無料で実装できるスパム対策(ハニーポットや Google reCAPTCHA の利用例)をご紹介します。以下はあくまで一例ですが、基本的な流れとコード例を交えて詳しく解説します。

1. Nuxt.js プロジェクトの作成と Firebase の初期設定

  • Nuxt.js プロジェクトの作成
    ターミナルで以下のコマンドを実行して新規プロジェクトを作成します。

    npx create-nuxt-app contact-form-demo
    cd contact-form-demo
    
  • Firebase プロジェクトの作成
    Firebase コンソールから新しいプロジェクトを作成し、Web アプリとして Firebase SDK の設定情報(apiKey, authDomain, projectId など)を入手します。
    ※お問い合わせ内容の記録方法として Firestore を使うか、Cloud Functions 経由でメール送信を行うかはプロジェクトの要件に合わせて選択してください。

  • Firebase の依存パッケージインストール
    Nuxt プロジェクト内で Firebase SDK を利用するために以下のコマンドを実行します。

    npm install firebase
    

2. Firebase の初期化と Nuxt での設定

  • プロジェクトルートに Firebase の設定ファイル(たとえば firebaseConfig.js)を作成し、以下のように初期化します。

    // firebaseConfig.js
    import { initializeApp } from "firebase/app";
    import { getFirestore } from "firebase/firestore";
    
    const firebaseConfig = {
      apiKey: "YOUR_API_KEY",
      authDomain: "your-project.firebaseapp.com",
      projectId: "your-project-id",
      storageBucket: "your-project.appspot.com",
      messagingSenderId: "YOUR_SENDER_ID",
      appId: "YOUR_APP_ID"
    };
    
    const app = initializeApp(firebaseConfig);
    const db = getFirestore(app);
    
    export { db };
    
  • 必要に応じて、Nuxt のプラグインとして登録し、グローバルに使えるようにしてもよいです(例えば、plugins/firebase.js を作成し、nuxt.config.js で登録)。

3. お問い合わせフォームの作成

  • ページコンポーネントの作成
    例として、pages/contact.vue を作成し、以下のようなフォームを実装します。

    <template>
      <div class="contact-container">
        <h1>お問い合わせフォーム</h1>
        <form @submit.prevent="submitForm">
          <!-- ハニーポット用の隠しフィールド -->
          <input type="text" name="website" v-model="honeypot" style="display: none;" />
    
          <div>
            <label for="name">お名前</label>
            <input type="text" id="name" v-model="form.name" required />
          </div>
          <div>
            <label for="email">メールアドレス</label>
            <input type="email" id="email" v-model="form.email" required />
          </div>
          <div>
            <label for="message">お問い合わせ内容</label>
            <textarea id="message" v-model="form.message" required></textarea>
          </div>
          <!-- オプション:reCAPTCHA 用の div (後述) -->
          <div id="recaptcha-container"></div>
          <button type="submit">送信</button>
        </form>
        <p>{{ statusMessage }}</p>
      </div>
    </template>
    
    <script>
    // Firestore にデータを保存する例
    import { collection, addDoc } from "firebase/firestore";
    import { db } from "~/firebaseConfig"; // プラグイン化している場合は context 経由にしてもよい
    
    export default {
      data() {
        return {
          form: {
            name: "",
            email: "",
            message: ""
          },
          // ハニーポットフィールド(通常のユーザーは入力しないはず)
          honeypot: "",
          statusMessage: ""
        };
      },
      methods: {
        async submitForm() {
          // ① ハニーポットチェック:通常のユーザーなら空のはず
          if (this.honeypot) {
            this.statusMessage = "不正な送信と判断しました。";
            return;
          }
    
          // ② オプション:reCAPTCHA のトークンを取得(後述)
          // const token = await grecaptcha.execute('YOUR_SITE_KEY', { action: 'submit' });
          // this.form.recaptchaToken = token;
    
          // ③ Firestore へ送信、または Cloud Functions 経由でメール送信
          try {
            await addDoc(collection(db, "contactSubmissions"), {
              name: this.form.name,
              email: this.form.email,
              message: this.form.message,
              timestamp: new Date().toISOString()
            });
            this.statusMessage = "送信が完了しました。";
            this.form = { name: "", email: "", message: "" };
          } catch (error) {
            console.error("Error submitting form:", error);
            this.statusMessage = "送信エラーが発生しました。";
          }
        }
      },
      mounted() {
        // ④ もし reCAPTCHA を利用する場合はスクリプトを読み込んで初期設定をする(v3 または v2)
        // 以下は reCAPTCHA v3 の例:
        // const recaptchaScript = document.createElement('script');
        // recaptchaScript.src = `https://www.google.com/recaptcha/api.js?render=YOUR_SITE_KEY`;
        // document.head.appendChild(recaptchaScript);
      }
    };
    </script>
    
    <style scoped>
    .contact-container {
      max-width: 600px;
      margin: 0 auto;
    }
    </style>
    
  • この例では、フォーム送信時にまず隠しハニーポットフィールド(honeypot)の値を確認しています。通常ユーザーはこのフィールドに何も入力しないため、値が入っている場合はスパムと判断します。[4]

4. 無料のスパム対策の追加方法

スパム対策として、以下の2つの手法が無料で実装可能です。

4.1. ハニーポットフィールド

  • すでに上記例のように、ユーザーには見えない隠しフィールド(<input type="text" name="website" …>)を追加します。
  • サーバー(もしくは Firestore への書き込み前)で、このフィールドが空であるかチェックするだけで簡単な対策となります。

4.2. Google reCAPTCHA の利用(v3 または v2)

  • 設定手順
    1. Google reCAPTCHA でサイトを登録し、サイトキーとシークレットキーを取得します。
    2. フロントエンド(上記の mounted() 内など)で reCAPTCHA のスクリプトを読み込み、フォーム送信時に grecaptcha.execute を利用してトークンを取得します。
    3. 取得したトークンをフォームデータに加え、Firebase Cloud Functions 経由で送信内容の検証を行います。Cloud Functions 内で reCAPTCHA の API にリクエストを送り、検証結果(success フラグなど)が true であるか確認してから処理を進めます。
  • なお、reCAPTCHA の利用自体は無料であり、ハニーポットとの併用でも十分な効果が期待できます。[11]

5. Firebase Cloud Functions を利用したメール送信(オプション)

もし、Firestore に保存するだけでなくメール送信も行いたい場合は、Firebase Cloud Functions と nodemailer を組み合わせる方法があります。以下は簡単な例です。

  • Cloud Functions 側のコード例(functions/index.js)

    const functions = require("firebase-functions");
    const nodemailer = require("nodemailer");
    
    // Gmail を例とした SMTP 設定(環境変数は Firebase の functions:config:set コマンドで設定)
    const transporter = nodemailer.createTransport({
      service: "gmail",
      auth: {
        user: functions.config().gmail.email,
        pass: functions.config().gmail.password
      }
    });
    
    exports.sendContactEmail = functions.https.onRequest(async (req, res) => {
      if (req.method !== "POST") {
        return res.status(405).send("Allowed only POST");
      }
      // reCAPTCHA の検証もここで追加可能
      const { name, email, message } = req.body;
    
      // ハニーポットの値確認(必須の場合)
      if (req.body.website && req.body.website.trim() !== "") {
        return res.status(400).send("Spam detected");
      }
    
      const mailOptions = {
        from: functions.config().gmail.email,
        to: "your-contact@yourdomain.com",
        subject: `お問い合わせ from ${name}`,
        text: `名前: ${name}\nメール: ${email}\n内容:\n${message}`
      };
    
      try {
        await transporter.sendMail(mailOptions);
        res.status(200).send("Email sent successfully");
      } catch (error) {
        console.error("Error sending email:", error);
        res.status(500).send("Internal Server Error");
      }
    });
    
  • Cloud Functions をデプロイした後、Nuxt 側からは通常の POST リクエストでこの Function を呼び出せます。

6. まとめ

  • Nuxt.js と Firebase(Firestore や Cloud Functions)を利用すれば、比較的シンプルにお問い合わせフォームを実装できます。
  • スパム対策は、まずハニーポットフィールドを追加するシンプルな方法があり、さらに Google reCAPTCHA を導入すれば、ボットからの自動送信をより確実に防げます。
  • プロジェクトの要件に合わせて、Firestore へ保存する方法と Cloud Functions 経由でメール送信する方法を組み合わせることで、運用に適したフォームが実現できます。

このように、Firebase と Nuxt.js を組み合わせることで、無料のサービス内でお問い合わせフォームとスパム対策を実現することが可能です。ぜひお試しください。


💖 ご支援いただけませんか?

スクリーンショット 2025-01-31 7.51.39.png

このブログでは、高品質な情報提供と学習活動を通じて、読者の皆さまのお役に立つことを目指しています。もしこの記事が役立ったと感じていただけましたら、ご支援いただけると幸いです!


暗号資産による寄付

以下のウォレットアドレスをご利用ください。重要:Ethereum (ETH)、BNB Chain (BNB)、Polygon (MATIC)、Avalanche (AVAX) は、全て以下の同一アドレスを使用しますが、送金ネットワークの選択を間違えると資金が失われます! 送金時には、絶対に使用するネットワーク(例: ERC-20、BEP-20、Polygon、Avalanche C-Chain)を必ず正しく選択してください。


Ethereum Logo

Ethereum (ETH) (ネットワーク: ERC-20)

0x5CDA2F68f59F641B00aD172475c3d5fC10321174
BNB Logo

BNB Chain (BNB) (ネットワーク: BEP-20)

0x5CDA2F68f59F641B00aD172475c3d5fC10321174
Polygon Logo

Polygon (MATIC) (ネットワーク: Polygon)

0x5CDA2F68f59F641B00aD172475c3d5fC10321174  
Avalanche Logo

Avalanche (AVAX) (ネットワーク: Avalanche C-Chain)

0x5CDA2F68f59F641B00aD172475c3d5fC10321174
Solana Logo

Solana (SOL)

EnPFbqDbF67rU9mAPvfgh4YYtncJNbFQ9NLQ5R6z5S2f
Stellar Logo

Stellar (XLM) メモ: 必要に応じて入力してください。

GCSMWCACKVEZ737GZAV4AJRFL52ZZKVQ7M3B3KYY64JJGOAO2GDYKABO 
Ripple Logo

Ripple (XRP) タグ: 必要に応じて入力してください。

r1s4EASr3zQRrfpDA3ptTahezBhGo2hhN
Cardano Logo

Cardano (ADA)

addr1q8heq6ddw8rwlqa5hqlucnfk36arah9tzc8ajxvu83870h7lrre25wzq9yemex857we56cm0xu8tmxqvm8nykmtgsjdqavdpv7
Dogecoin Logo

Dogecoin (DOGE)

DRFZ9JhAk3DTtu1tV85cawekWNrm1vKm3H

資金用途

寄付金は以下の目的で活用させていただきます:

  1. サーバー維持費やデザインツール購入
  2. 学習活動(オンラインコース受講・書籍購入)
  3. 読者向け無料コンテンツ制作

ご協力いただいた皆さまには心より感謝申し上げます! 🙏


補足情報

  • Ethereum (ETH)、BNB Chain (BNB)、Polygon (MATIC)、Avalanche (AVAX)について
    上記4つのネットワークは同じウォレットアドレス0x5CDA2F68f59F641B00aD172475c3d5fC10321174)を使用します。ただし、送金時には、絶対に使用するネットワーク(例: ERC-20、BEP-20、Polygon、Avalanche C-Chain)を必ず正しく選択してください。

  • USDCやUSDTなどのステーブルコインも、対応するネットワーク経由であれば送金可能です。ただし、送金先のネットワークと選択するネットワークが一致していることを必ず確認してください。

  • 初回送金時には少額でテスト送金することをおすすめします。


2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?