LoginSignup
0
0

Next.jsとServerless Functions、blastengineを使ってお問い合わせフォームを作る

Posted at

Next.jsはReactをフロントエンドに利用したアプリケーションフレームワークです。Vercel向けにアプリケーションを開発する際によく使われています。

今回はそんなNext.jsとServerless Functions、blastengineを使ってお問い合わせフォームを実装します。

コード

今回のコードは https://github.com/blastengineMania/nextjs-contact にアップしてあります。実装時の参考にしてください。

実装について

フロントエンド

フロントエンドはNext.jsで実装します。入力項目は以下の通りです。

項目 変数名
会社名 テキスト company
名前 テキスト name
メールアドレス テキスト mailaddress
問い合わせ種別 選択 type
問い合わせ内容 テキスト body

コードは以下のようになります。

ステートの準備

今回は以下のステートを用意しています。

// フォーム入力データの型
type formData = {
  company: string;
  name: string;
  email: string;
  type: string;
  message: string;
};

// フォームの選択肢の型
type SelectOption = {
  label: string;
  value: string;
};

// フォームの初期値
const defaultValue: formData = {
  company: '',
  name: '',
  email: '',
  type: '',
  message: '',
};

const options: SelectOption[] = [
  { label: 'お問い合わせ', value: 'お問い合わせ' },
  { label: 'ご意見', value: 'ご意見' },
  { label: 'その他', value: 'その他' },
];

const [form, setForm] = useState(defaultValue);

選択項目の表示

選択項目は options で、動的に生成しています。

<div className="block type">
	<label htmlFor="frm-type">お問い合わせ種別</label>
	<select
		onChange={(e) =>
			setForm({ ...form, ...{ type: e.target.value } })
		}
	>
		{options.map((option) => (
			<option value={option.value}>{option.label}</option>
		))}
	</select>
</div>

フォームについて

フォームは以下のようになります。入力されたら、その値をフォームに反映しています。ボタンやフォームの送信時には send 関数を呼んでいます。

<>
	<div className={styles.container}>
		<Head>
			<title>お問い合わせ</title>
		</Head>

		<main className={styles.main}>
			<h3 className={styles.title}>お問い合わせ</h3>
			<form className="container" onSubmit={send}>
				<div className="company block">
					<label htmlFor="frm-company">会社名</label>
					<input
						id="frm-company"
						type="text"
						value={form.company}
						autoComplete="company"
						onChange={(e) =>
							setForm({ ...form, ...{ company: e.target.value } })
						}
						required
					/>
				</div>
				<div className="account block">
					<label htmlFor="frm-name">名前</label>
					<input
						id="frm-name"
						type="text"
						value={form.name}
						autoComplete="name"
						onChange={(e) =>
							setForm({ ...form, ...{ name: e.target.value } })
						}
						required
					/>
				</div>
				<div className="email block">
					<label htmlFor="frm-email">メールアドレス</label>
					<input
						id="frm-email"
						type="email"
						value={form.email}
						autoComplete="email"
						onChange={(e) =>
							setForm({ ...form, ...{ email: e.target.value } })
						}
						required
					/>
				</div>
				<div className="block type">
					<label htmlFor="frm-type">お問い合わせ種別</label>
					<select
						onChange={(e) =>
							setForm({ ...form, ...{ type: e.target.value } })
						}
					>
						{options.map((option) => (
							<option value={option.value}>{option.label}</option>
						))}
					</select>
				</div>
				<div className="message block">
					<label htmlFor="frm-message">Message</label>
					<textarea
						id="frm-message"
						rows={6}
						value={form.message}
						onChange={(e) =>
							setForm({ ...form, ...{ message: e.target.value } })
						}
					>
					</textarea>
				</div>
				<button onClick={send}>送信</button>
			</form>
		</main>
	</div>
</>

また、CSS(global.css)は以下のようになります。

html,
body {
  padding: 0;
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
    Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
  background: #1e1e1e;
  min-height: 100vh;
  display: flex;
  color: rgb(243, 241, 239);
  justify-content: center;
  align-items: center;
}

.block {
  display: flex;
  flex-direction: column;
}

.name {
  display: flex;
  flex-direction: row;
  justify-content: space-between;
}

.container {
  font-size: 1.3rem;
  border-radius: 10px;
  width: 85%;
  padding: 50px;
  box-shadow: 0 54px 55px rgb(78 78 78 / 25%), 0 -12px 30px rgb(78 78 78 / 25%),
    0 4px 6px rgb(78 78 78 / 25%), 0 12px 13px rgb(78 78 78 / 25%),
    0 -3px 5px rgb(78 78 78 / 25%);
}

.container input {
  font-size: 1.2rem;
  margin: 10px 0 10px 0px;
  border-color: rgb(31, 28, 28);
  padding: 10px;
  border-radius: 5px;
  background-color: #e8f0fe;
}

.container select {
  font-size: 1.2rem;
  margin: 10px 0 10px 0px;
  border-color: rgb(31, 28, 28);
  padding: 10px;
  border-radius: 5px;
  background-color: #e8f0fe;
}

.container textarea {
  margin: 10px 0 10px 0px;
  padding: 5px;
  border-color: rgb(31, 28, 28);
  border-radius: 5px;
  background-color: #e8f0fe;
  font-size: 20px;
}

.container h1 {
  text-align: center;
  font-weight: 600;
}

.name div {
  display: flex;
  flex-direction: column;
}

.block button {
  padding: 10px;
  font-size: 20px;
  width: 30%;
  border: 3px solid black;
  border-radius: 5px;
}

.button {
  display: flex;
  align-items: center;
}

textarea {
  resize: none;
}

Untitled.jpeg

送信した際の処理

送信ボタンを押した際の処理として、入力情報を POST /api/blastengine に送信します。

// フォームの送信処理
const send = async (e: any) => {
  e.preventDefault();
  fetch('/api/blastengine', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(form),
  });
};

Serverless Functions

まず .env ファイルを作成して、以下の2つの環境変数を定義します。

環境設定名
BLASTENGINE_USER_ID 先ほど作成したAPIユーザー名
BLASTENGINE_API_KEY 先ほど作成したAPIキー

コードの作成

ライブラリの追加

プロジェクトへ、blastengine SDKを追加します。

npm i blastengine

基本形

コードは /pages/api/blastengine.ts として作成します。基本形は以下の通りです。

import type { NextApiRequest, NextApiResponse } from 'next'
import { BlastEngine, Mail } from 'blastengine';

type Data = {
  delivery_id: number
}

export default async function handler(
    req: NextApiRequest,
    res: NextApiResponse<Data>
  ) {
	// この中に処理を書く
}

SDKの初期化

読み込んだblastengineのSDKを初期化します。

new BlastEngine(process.env.BLASTENGINE_USER_ID!, process.env.BLASTENGINE_API_KEY!);

件名・本文を用意する

続けてメール送信する内容を準備します。

const { body } = req;
const mail = new Mail();
const text = `__USERNAME__様

お問い合わせいただきありがとうございます。内容を確認し、追ってご連絡いたします。

会社名:
__COMPANY__
お名前:
__USERNAME__
お問い合わせ内容:
__MESSAGE__
`;
mail
  .setFrom('info@blastengine.jp', '管理者')
  .setSubject('お問い合わせありがとうございます')
  .addTo(body.email, {
		USERNAME: body.name,
		COMPANY: body.company,
		MESSAGE: body.message
	})
  .setText(text)
  .setEncode('UTF-8');

最後にメールを送信します。レスポンスとして、blastengineから返却される配信ID(デリバリーID)を返します。

await mail.send();
res.status(200).json({ delivery_id: mail.deliveryId! });

スクリプト全体は以下のようになります。

import type { NextApiRequest, NextApiResponse } from 'next'
import { BlastEngine, Mail } from 'blastengine';

type Data = {
  delivery_id: number
}

export default async function handler(
    req: NextApiRequest,
    res: NextApiResponse<Data>
  ) {
  new BlastEngine(process.env.BLASTENGINE_USER_ID!, process.env.BLASTENGINE_API_KEY!);
  const { body } = req;
  const mail = new Mail();
  const text = `__USERNAME__様

お問い合わせいただきありがとうございます。内容を確認し、追ってご連絡いたします。

会社名:
__COMPANY__
お名前:
__USERNAME__
お問い合わせ内容:
__MESSAGE__
`;
  mail
    .setFrom('info@blastengine.jp', '管理者')
    .setSubject('お問い合わせありがとうございます')
    .addTo(body.email, { USERNAME: body.name, COMPANY: body.company, MESSAGE: body.message })
    .setText(text)
    .setEncode('UTF-8');
  await mail.send();
  res.status(200).json({ delivery_id: mail.deliveryId! });
}

uploading...0

まとめ

今回はNext.jsとServerless Functionsを使って、お問い合わせフォームを作成しました。フロントエンドだけでなく、API実行部分もNext.js内で使えるのが便利です。

エンジニア向けメール配信システム「ブラストエンジン(blastengine)」

0
0
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
0
0