1. はじめに
フロントエンドの開発をしていると、登録フォームの実装で「任意項目が空のとき、APIにどう送信すればいいんだろう?」と悩むことありませんか?
例えば、ユーザー登録画面で以下のような項目があったとします:
- ユーザー名(必須)
- ニックネーム(任意)
- 年齢(任意)
ニックネームが未入力のとき、APIに以下のどちらで送るべきでしょうか?
// パターンA: 空文字として送る
?username=taro&nickname=&age=25
// パターンB: パラメータごと送らない
?username=taro&age=25
2. 任意項目が空なら送らないのが一般的
最初に結論からお伝えします。任意項目が空の場合は、パラメータごと送らないのが一般的です。
// ❌ 空文字を含める(非推奨)
?username=taro&nickname=&age=25
// ⭕️ 空の項目は送らない(推奨)
?username=taro&age=25
次のセクションから、なぜこの方法が推奨されるのか、その理由を詳しく見ていきましょう。
3. なぜ「送らない」のが推奨されるのか?
3-1. 意図が明確に伝わる
空文字を送る方法では、「値がない」ことと「空文字という値がある」ことを区別できません。一方、パラメータ自体を送らない方法なら、「ユーザーが何も入力しなかった」という意図が明確に伝わります。
// バックエンド側での判断
// パターンA: 空文字が送られてきた場合
if (nickname === '') {
// これは「空文字を入力した」のか「未入力」なのか?
}
// パターンB: パラメータが存在しない場合
if (nickname === undefined) {
// 明らかに「未入力」だと分かる
}
この違いは、特に検索機能で重要になります。例えば、商品検索で「名前が空文字のものを探す」のと「名前での絞り込みをしない」のは、全く異なる動作です。
3-2. 無駄なデータ送信を避ける
空のパラメータを送らないことで、以下のメリットがあります:
- ネットワークトラフィックの削減:データ量が減り、通信が効率的になります
- URLが短く読みやすくなる:ログやデバッグ時に見やすくなります
- セキュリティ上も有利:不要な情報を送らないことで、攻撃対象を減らせます
3-3. バックエンドの実装がシンプルに
バックエンド側では、「パラメータが存在しない = デフォルト値を使う」という実装が書きやすくなります。
// バックエンド側の実装例
const nickname = params.nickname ?? 'ゲスト'; // 未入力ならデフォルト値
const age = params.age; // 送られてきたら使う
この実装パターンは、Null合体演算子(??)やOptional Chainingなど、現代のプログラミング言語の機能とも相性が良く、コードがシンプルで読みやすくなります。
3-4. 業界標準のベストプラクティス
API設計の世界では、「パラメータの存在/不在でデータの状態を表現する」という考え方が広く受け入れられています。OpenAPI仕様でもoptional/nullableの扱いが定義されており、多くのAPI設計ガイドラインで「任意項目が空の場合はパラメータを送らない」ことが推奨されています。
4. 実装方法:基本編
それでは、実際にどうコードを書けばいいのか見ていきましょう。
4-1. シンプルな実装
最も基本的な実装方法は、条件分岐で空の項目をチェックすることです。
const registerUser = async (formData: FormData) => {
// 送信するパラメータを準備
const params: Record<string, any> = {
username: formData.username, // 必須項目は必ず含める
};
// ニックネームが入力されている場合のみ追加
if (formData.nickname && formData.nickname.trim() !== '') {
params.nickname = formData.nickname;
}
// 年齢が入力されている場合のみ追加
if (formData.age !== undefined && formData.age !== '') {
params.age = formData.age;
}
// APIにリクエスト送信
await api.post('/users', params);
};
この方法は分かりやすいですが、項目が増えると条件分岐が増えて見通しが悪くなります。
4-2. より実用的な実装
ヘルパー関数を用意して共通化する方法もあります。
// utils/apiHelpers.ts
/**
* オブジェクトから空の値を持つプロパティを除外する
* @param data 処理対象のオブジェクト
* @returns 空でない値のみを含む新しいオブジェクト
*/
export const removeEmptyFields = <T extends Record<string, any>>(
data: T
): Partial<T> => {
return Object.entries(data).reduce((acc, [key, value]) => {
// 除外する値のパターン
// - 空文字 ('')
// - null
// - undefined
// ※ 0 は意味のある値なのでそのまま残す
if (value === 0 || (value !== '' && value !== null && value !== undefined)) {
acc[key] = value;
}
return acc;
}, {} as Partial<T>);
};
4-3. 使用例
import { removeEmptyFields } from './utils/apiHelpers';
const formData = {
username: 'taro',
nickname: '', // 空文字
age: 25,
bio: null, // null
score: 0, // 0点(これは残す)
};
const params = removeEmptyFields(formData);
console.log(params);
// 結果: { username: 'taro', age: 25, score: 0 }
// nickname と bio は除外されている
このヘルパー関数を使えば、プロジェクト全体で一貫した処理ができます。新しいメンバーが参加しても迷うことがありませんし、コードレビューもスムーズになります。
5. 数値型の任意項目はどうする?
文字列の場合は比較的分かりやすいのですが、数値型の任意項目はどう扱えばいいのでしょうか。
5-1. 0をデフォルトとして送る?
// ❌ こうすると問題が起きる
const formData = {
username: 'taro',
age: 0, // 未入力時に0を設定
};
この実装には大きな問題があります。「0」は意味のある値なので、「未入力」なのか「0と入力した」のか区別できなくなってしまうのです。
5-2. 0が意味を持つ具体例
以下のような場面で、0は重要な意味を持ちます:
// 年齢の例
age: 0 // 「0歳の赤ちゃん」を表す?それとも「年齢未入力」?
// 価格の例
price: 0 // 「無料(0円)」?それとも「価格未設定」?
// 在庫数の例
stock: 0 // 「在庫なし(売り切れ)」?それとも「在庫数未入力」?
// 評価点の例
rating: 0 // 「最低評価(0点)」?それとも「未評価」?
これらのケースでは、「0」と「未入力」を明確に区別する必要があります。
5-3. 推奨される実装
数値型の任意項目も、未入力の場合は送らないのが適切です。
const formData = {
username: 'taro',
age: undefined, // 未入力(送信しない)
score: 0, // 0点と明示的に入力した(送信する)
rating: null, // 未評価(送信しない)
};
const params = removeEmptyFields(formData);
// 結果: { username: 'taro', score: 0 }
5-4. フロントエンドとバックエンドでの判断
フロントエンド側
// 未入力の場合
const formData = {
username: 'taro',
age: undefined, // または null を設定
};
// 0を入力した場合
const formData = {
username: 'taro',
age: 0, // 明示的に0を送る
};
バックエンド側
// Node.js/Express の例
// パラメータが存在しない場合
if (req.body.age === undefined) {
// 未入力 → デフォルト値を使う or nullとしてDBに保存
user.age = null;
}
// 0が送られてきた場合
if (req.body.age === 0) {
// ユーザーが「0」と入力した → そのまま保存
user.age = 0;
}
このように、パラメータの「存在」「不在」で判断することで、「未入力」と「0を入力」を明確に区別できます。
6. まとめ
この記事では、API設計における任意項目の扱い方について解説しました。重要なポイントをおさらいしましょう。
基本原則
- 任意項目が空の場合は送信しないのが一般的
- 0は意味のある値として送信する
- 「送られてこない = 未入力」という考え方がAPI設計の基本
メリット
- データの意図が明確に伝わる
- 無駄なデータ送信を避けられる
- バックエンドの実装がシンプルになる
- データベースとの整合性が保ちやすい
実装のコツ
- ヘルパー関数を共通化してチーム全体で使用する
- コーディング規約やAPI仕様書に明記する
- テストコードでエッジケースをカバーする
- レビュー時のチェックポイントを明確にする
実際のプロジェクトで実装する際は、チームメンバーと認識を合わせながら、一貫性のあるコードを心がけていきましょう。