Next.jsで問い合わせフォームを作成してVercelにデプロイする（Nodemailer）

<h2>問い合わせフォームの作成</h2>

Next.jsでNodeMailerを使ってコンタクトフォームを作成していく。Vercelにアップロードしたいけど、何やらできないみたいな記事をいくつか見かけたが全然できる。公式でもNodemailerは使えると書いてある。ただPostmarkやResend、SendGridやAWSSESなどのサードパーティーメールサービスを使用した方が分析等しやすいためおすすめとは書かれている。

https://vercel.com/guides/sending-emails-from-an-application-on-vercel

#### 前提
next.js14
npx create-next-app
typescriptなどは全部yes


nodemailerをインストール
```php:
npm install nodemailer @types/nodemailer
```


<h2>コードを書いていく</h2>

my-appフォルダ配下(.gitignoreと同列)に.env.localファイルを作成して
```php:.env.local
NODEMAILER_EMAIL="aaaaaaa.gmail.com"
NODEMAILER_PASSWORD="bbbb bbbb bbbb bbbb"
```

:::note warn
NODEMAILER_EMAIL、NODEMAILER_PASSWORDは任意の文字で大丈夫だがAPIの呼び出し(route.ts)に合わせる必要がある。なお今回はgmailを使用する。
:::

NODEMAILER_EMAILはGoogleアカウントのメールアドレスを使用。
NODEMAILER_PASSWORDはGoogleアカウントを検索し→セキュリティ→2段階認証プロセス→アプリ パスワードで新しく作成したものを使用する。









<h2>そんなわけでフロントエンド部分</h2>
npx create-next-appでいならないところを削除して簡易的なものを作成



```php:app/page.tsx
import ContactForm from "./components/ContactForm"


const page = () => {
  return (
    <main className="flex grow flex-col items-center justify-center">
      <div className="space-y-4">
        <h1 className="text-4xl">Contact Us</h1>
        <ContactForm />
      </div>
    </main>
  )
}

export default page

```

上記のContactFormはcomponentsで別途作成して呼び出している

```php:app/components/ContactForm.ts
"use client"

import { useState } from "react"

function ContactForm() {
  const [email, setEmail] = useState("")
  const [message, setMessage] = useState("")

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault()

    await fetch("/api/email", {
      method: "POST",
      body: JSON.stringify({ email, message }),
    })
  }

  return (
    <div>
      <h2>Contact Us</h2>
      <form onSubmit={handleSubmit} className="space-y-10">
        <div>
          <label>Email:</label>
          <input
            type="email"
            value={email}
            onChange={(e) => setEmail(e.target.value)}
            required
          />
        </div>
        <div>
          <label>Message:</label>
          <textarea
            value={message}
            onChange={(e) => setMessage(e.target.value)}
            required
          ></textarea>
        </div>
        <button type="submit">Submit</button>
      </form>
    </div>
  )
}

export default ContactForm

```

<h2>Apiを実装していく</h2>



Nodemailerのexampleは参考までに

https://nodemailer.com/


app/api/email/route.tsにapiを作成していく

なおNext.js13以降でApiを使用する場合デフォルト値はapp/api/任意の文字/route.tsで、もし色々なApiを実装したいならばapiの配下にApiのフォルダが沢山作っていく感じ。それぞれのページングする際にディレクトリにpage.tsxを各々に作っていく感じと似たようなものと覚えておこう。


```php:app/api/email/route.ts
import { NextResponse, type NextRequest } from "next/server"
import nodemailer from "nodemailer"
import Mail from "nodemailer/lib/mailer"
import { env } from "process"//.envからの呼び出し

export async function POST(request: NextRequest) {
  const { email, message } = await request.json()

  const transport = nodemailer.createTransport({
    service: "gmail",
    auth: {
      user: process.env.NODEMAILER_EMAIL,
      pass: process.env.NODEMAILER_PASSWORD,
    },
  })

  const mailOptions: Mail.Options = {
    from: env.NODEMAILER_EMAIL,
    to: env.NODEMAILER_EMAIL,
    subject: `Message from お問い合わせ(${email})`,
    text: message,
  }

  try {
    await transport.sendMail(mailOptions)
    return NextResponse.json({ message: "Success!", status: 200 })

  } catch (err) {
    return NextResponse.json({ message: "Failed!", status: 500 })
  }
}
```






メインページpage.tsxのsubmitボタンを押すとapiのemailが呼び出される。


:::note warn
<h2>参考までにApiを叩いたものの確認の仕方</h2>
apiの呼び出し方。jsonを返すようなAPIを作成

localhost:3000/api/helloでapiを呼び出すことができるようにしたい。

```php:src/app/api/hello/route.js（version13以降の書き方）

import { NextResponse } from "next/server"

export async function GET() {
    return NextResponse.json("Hello!")//({name:"tanaka"})なども使える
    
}
```

```php:/pages/api/hello.js（version12までの書き方）


export default function hello(req, res){
    return res.json("Hello!")
}
```

参照ページ

https://monotein.com/blog/nextjs-version-13-migration

:::


<h2>メール送信後（API呼出後）ページ遷移する方法</h2>

呼び出した後の処理はbuttonタグの中にonClickイベントを追加、useRouterを使用してrouter.pushすることでページ遷移が可能になる.参照は以下サイト

https://techblg.app/articles/how-to-transfer-to-different-page-without-link/

https://prettytabby.com/nextjs13-router/



```php:app/api/email/route.tsの完成版（クリック後ページ移行）
"use client"

import { useRouter } from "next/navigation"
import { useState } from "react"

function ContactForm() {
  const router = useRouter();
  const [email, setEmail] = useState("")
  const [message, setMessage] = useState("")

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault()

    await fetch("/api/email", {
      method: "POST",
      
      body: JSON.stringify({ email, message }),
    })
  }

  return (
    <div>
      <h2>Contact Us</h2>
      <form onSubmit={handleSubmit} className="space-y-10">
        <div>
          <label>Email:</label>
          <input
            type="email"
            value={email}
            onChange={(e) => setEmail(e.target.value)}
            required
          />
        </div>
        <div>
          <label>Message:</label>
          <textarea
            value={message}
            onChange={(e) => setMessage(e.target.value)}
            required
          ></textarea>
        </div>
        <button type="submit" onClick={()=>router.push("/page")}>Submit</button>
      </form>
    </div>
  )
}

export default ContactForm
```

現在/pageは作成していないのでページエラーが出る。適当にページを作ってね。

ハマった所として下記のようにしようとしたら失敗した
NextRouter was not mounted.

```php:
import { useRouter } from "next/router";

export default function IndexPage() {
  const router = useRouter(); //useRouterフックを定義して
  return (
    <div>
      Hello World.{" "}
      <button onClick={() => router.push("/about")}> // router.pushで遷移したいページを指定するだけ
        go to about
      </button>
    </div>
  );
}
```

上記はuseRouterをnext/routerではなくnext/navigationからuseRouterをimportすることで、エラーが解消されれる。また、URLクエリパラメータを取得するにはuseRouterではなくuseSearchParamsをimportする必要がある。






参照
https://blog.stackademic.com/automate-200-emails-daily-nodemailer-next-js-13-integration-c7773ab63d5d

<h2>Vercelでの環境設定</h2>
上記で作成したコードはhttp://localhost:3000/で実行できたと思う。Submitボタンを押せばメールが送信される。なおメールの発信（From）は自分で設定したGoogleアカウントのメールアドレスからの発信となることを覚えておこう。（届いたメールを安易に返信しても自分に届くだけである）

さて本題のVercelの設定だが、コードをデプロイして環境設定をするくらいである。環境設定の仕方は至極簡単。
先程作成した.env.localファイルをEnvironment Variablesに設定する。SettingにEnvironment Variablesの項目があるので


```php:.env.local
NODEMAILER_EMAIL="sukeve@gmail.com"
NODEMAILER_PASSWORD="bskx zchv  "
```

上記のようなenvファイルなら（env.localなどもOK）
KeyにNODEMAILER_EMAIL　Valueにsukeve@gmail.comと
keyにNODEMAILER_PASSWORD Valueにbskx zchvを記入すればよい。

---

恐らくNext.jsの更新によりNodemailerが使用できなかったのが使用できるようになったのでしょう。是非とも問い合わせフォームを自作できるようにしていこう