諸事情により実際の処理に類似するお悩みに置き換えて記事を掲載してます。ご了承ください。
ボタンが押せない夜に泣いた話
APIでデータを取得して、それをフォームに入れて送信する──そんな簡単そうな作業で、私はまさかの深夜2時まで格闘する羽目になりました。
「Ajaxって誰?」「非同期って何が同期してないの?」
目の前の送信ボタンは押せるのに、反応はゼロ。コンソールにはCORSだのPromiseだの、見慣れない英語のオンパレード。
今回はそんな初心者エンジニアの私が「送信ボタンが押せない夜」をどう乗り越えたのかを、実体験ベースでゆるく・わかりやすく・技術的にも整理しながらお届けします。
Ajaxって誰?APIって何?用語でつまずく序章
そもそも、私が最初につまずいたのは「用語」でした。
技術系のドキュメントを読むと、当たり前のように登場するAjaxとAPIという言葉。でも、当時の私は正直こんな認識でした。
- Ajax:なんかページをリロードしないでデータ送れるやつ?
- API:住所データとかくれる便利なサーバー?
どちらも正解のようで、説明としては不十分。少しだけ丁寧に整理してみましょう。
Ajaxとは?
「Asynchronous JavaScript + XML」の略で、JavaScriptを使って、サーバーと非同期通信する技術の総称です。
たとえば、ボタンを押すと裏でデータを送って結果だけ画面に反映する──みたいな処理がAjax。ページ全体をリロードせずに部分的な更新ができるのが強みです。
APIとは?
API(Application Programming Interface)は、他のプログラムに機能やデータを提供する窓口のような存在です。
たとえば郵便番号→住所変換のように、外部のデータベースとやり取りして答えを返してくれるのがAPI。自分のサイトからその窓口(エンドポイント)にリクエストを送って、データをもらう形になります。
…と、こうして整理すると「なるほど」と思えるのですが、最初はこの2つの役割がごっちゃになっていて、「何がどう動いてるのか」がさっぱり見えなかったのです。
やりたかったこと:APIの結果をフォームに使いたい
私がやりたかったことは、ただそれだけでした。
「郵便番号を入力したら住所が自動で入る」──それを実現して、そのままフォームを送信できればOK。
多くのサイトで見かけるし、「きっと簡単にできるだろう」と思っていました。
実現したい流れは、こんな感じでした。
- ユーザーが郵便番号を入力する
- JavaScript(Ajax)でAPIにリクエストを送る
- 取得した住所データをフォームの住所欄に自動入力する
- ユーザーが他の項目を記入し、送信ボタンを押す
文章にすると超シンプル。ところがこれが、やってみると全然うまく動かない。
「住所は出るのに送信されない」「送信すると住所が空欄」など、想像以上に地味で泥臭いバグの連続でした。
「なぜ?」を探っていくと、そこには非同期処理とCORSという2つの大きな壁が立ちはだかっていたのです…。
詰んだポイント①:非同期処理って何?いつの間に?
APIから住所を取得してフォームに入れる──それは成功しているように見えました。
でも、送信ボタンを押しても、住所の欄が空欄のまま送られてしまう。
なぜだ…?と調べていくうちに、出てきたのが「非同期処理」という言葉。
結論から言えば、こういうことでした。
APIから住所を取得 → 処理が終わる前にsubmitが発火してしまう → フォームにはまだデータが入っていない
つまり、「API呼び出し完了を待たずにフォームが送信されてしまっていた」わけです。
同期と非同期のイメージ
– 同期: 「終わってから次に進む」
– 非同期: 「終わるのを待たずに次に進む」
JavaScriptのAPI通信(fetchやXMLHttpRequestなど)は基本非同期。
「API呼び出し → 結果を受け取って入力 → フォーム送信」の流れを実現するには、非同期処理をきちんと制御する必要があります。
初心者の私は…
とりあえず setTimeout
で2秒待ってからsubmitしようとしたり、then()
チェーンの中にsubmitを無理やり書いてみたり…
最終的には async/await
に辿り着いたものの、そこに行き着くまでにだいぶ心が折れました。
そして、次に出会ったのがCORSエラー。
JavaScript初心者を確実に混乱させる「もうひとつの地獄」がそこに待っていました。
詰んだポイント②:CORSエラーってなんなん?
非同期処理がある程度理解できてきて、「よし、あとはAPI呼び出し!」と思った矢先に現れたのが、
ブラウザのコンソールに表示された「CORS policyでブロックされました」というエラーメッセージ。
Access to fetch at 'https://example.com/api' from origin 'http://localhost:3000' has been blocked by CORS policy...
読んでもよくわからないけど、とりあえずブロックされてるということだけはわかる。
「CORSって何?私なにか悪いことした?」と困惑しました。
CORSとは?
CORS(Cross-Origin Resource Sharing)は、異なるドメイン間でのリソース共有に関するセキュリティ仕様です。
たとえば、localhost
から外部のAPI(たとえばhttps://api.example.com
)にアクセスすると、「この外部リクエスト、許可されてないよ!」とブラウザに怒られるわけです。
なぜCORSエラーになるの?
原因は大きく分けて2つあります。
- APIサーバーが、リクエスト元のドメインを許可していない
- HTTPメソッドやヘッダーの内容が“プリフライト検証”でNGになっている
初心者からすると、「せっかくfetchで書けたのに、サーバー側で弾かれる」という理不尽さすら感じました…。
私がやった対処法
- ドキュメントで「CORS対応してます」と書かれているAPIを使うようにした
- テスト中は一時的にCORS回避プロキシ(例:
https://cors-anywhere.herokuapp.com/
)を使って実験 - 最終的には自分のサーバー側でリクエストを中継して安全に処理
超簡単にまとめると、同じドメインにajaxを記載したファイルと、呼び出すファイルを置いたらどうにかなった。
CORSは「フロントエンドだけでどうにかできる問題じゃない」と知った瞬間でもありました。
こうして、「非同期処理」と「CORS」という二大トラップをくぐり抜け、ようやく私は送信ボタンを機能させることに成功したのです。
結局どうすれば動いたのか?(成功パターン紹介)
非同期処理のタイミングに悩み、CORSに泣き、心折れかけながらも、
ようやく送信ボタンがちゃんと動いてくれたときのコードがこちらです。
document.getElementById('zip').addEventListener('blur', async () => {
const zip = document.getElementById('zip').value;
try {
const response = await fetch(`https://api.zipaddress.net/?zipcode=${zip}`);
const data = await response.json();
if (data.code === 200) {
document.getElementById('address').value = data.data.fullAddress;
} else {
alert('住所が見つかりませんでした');
}
} catch (error) {
console.error('APIエラー:', error);
alert('住所取得に失敗しました');
}
});
document.getElementById('form').addEventListener('submit', (e) => {
e.preventDefault();
// 住所がちゃんと入っているかを確認してから送信
const address = document.getElementById('address').value;
if (!address) {
alert('住所が入力されていません');
return;
}
// 手動で送信処理(例:AJAX送信やform.submit()など)
e.target.submit();
});
重要なのはこの2つです。
- 住所の取得は非同期(async/await)で待つ
- フォームの送信はイベントで一度止めて、必要条件を満たしたら送る
実際には上記コードのまま使えるわけではなく、APIの仕様に合わせて調整が必要ですが、
基本の流れとしては「取得を待って、値を確認してから送信」という考え方が超大事です。
これがわかると、「非同期処理って怖くないかも」と思えるようになってきます。
それでもハマる?次に備える3つのヒント
今回の件で得た教訓は「理解してるつもりが一番こわい」ということでした。
今後また似たような場面に出会っても慌てないために、自分なりに整理した“次へのヒント”を共有します。
1. console.logで段階を追え
非同期処理は“見えないタイミング”との戦いです。console.log()
を挟んで「いつどの値が取れているか」を確認するだけで、かなり頭が整理されます。
console.log('郵便番号を取得しました:', zip);
console.log('APIから返ってきたデータ:', data);
ログを出す場所と順番を意識するだけでも、バグの発見率は段違いです。
2. フロー図を書いてみる
文字だけで処理の流れを把握しようとすると混乱します。
ホワイトボードや紙、NotionやFigmaでもOK。APIコール→反映→送信…という処理の流れを図にしてみると、「今どこで止まってるか?」が掴めやすくなります。
3. APIじゃなくJSONでまず練習
APIは便利ですが、CORSや通信エラーといった外部要因に振り回されがち。
最初はローカルに置いたsample.json
などからデータを読み込んで、「fetchの動き」に慣れるのがおすすめです。
fetch('./sample.json')
.then(res => res.json())
.then(data => console.log(data));
“APIで動かない”のがfetchのせいなのか、サーバーのせいなのか、自分の書き方のせいなのかを切り分ける手段としても有効です。
まとめ:誰でも最初は“押せない夜”がある
「なんでボタン押しても送れないの?」「住所が入らない!」「CORSってなに?」
そんな初心者ならではの疑問とイライラを、私もたっぷり味わいました。
それでもひとつずつ問題を整理していけば、ちゃんとフォームは送れるようになります。
今回つまずいたポイントを振り返ると、次の3つがキモでした。
- 非同期処理のタイミングを正しく理解する
- フォーム送信を制御して流れを作る
- CORSエラーは「自分のせいじゃないこと」もあると知る
今でも思います。「初心者向け」って書かれてるコードでも、
エラーが出れば心が折れそうになるし、理屈じゃなくて“動かない”現実に泣かされます。
でも、それを乗り越えたときの「ちゃんと送れた!」の喜びは、他の何にも代えがたい体験
おまけ
郵便番号から住所自動入力はこのへん使えば簡単にできる。
- フォーム自体をmailformproにしちゃう
- ライブラリを使う。※検索すれば方法は山程でてくる

HN:雄飛
職業:
- フリーランスエンジニア
- 某社システム統括部門長
保有資格:
- 応用情報処理技術者
- 第二種情報処理技術者
- 情報セキュリティマネジメント
- CCNA