17ステップ
すべての段階 / 17件を表示中
STEP 0
基礎
APIとは何か ── 学ぶ順番を決める
目安 1〜2日
このステップの到達点 ── 「API」を、技術の名前ではなくサービス同士の受付窓口 として説明できる。そして、これから学ぶ順番がなぜこの順番なのか を言える。ここができていれば、あとの16ステップは地図に地名を書き込んでいく作業になります。
APIとは「機械のための受付窓口」
API (Application Programming Interface)とは、あるサービスが「ここに、この形式で頼んでくれれば、この形式で返します」とあらかじめ公開している窓口 のことです。人間向けの窓口が画面(Webサイトやアプリ)なら、プログラム向けの窓口がAPI です。
レストランで考えると分かりやすくなります。厨房(データベースや業務ロジック)に客が直接入ることはできません。客はメニュー (=APIドキュメント)を見て、店員 (=API)に注文します。店員は決まった書き方の伝票で厨房に伝え、料理を持って帰ってきます。ここで大事なのは、客は厨房の作り方を知らなくてよい ということです。これがAPIの本質で、カプセル化 と呼ばれる考え方です。
いちばん大まかな全体像
あなたのプログラム
相手のサービス
あなたの
アプリ・画面
①
通信
(HTTPS)
②
APIの
受付窓口
③
相手のDB・
業務ロジック
④
① 何を頼むか決める(リクエストを組み立てる)
② インターネットを通って相手に届く(暗号化される)
③ 相手が「あなたが誰か」を確かめ、頼みを処理する(認証・認可)
④ 結果をJSONという形式に整えて返す → ①に戻って受け取る
APIを学ぶと、何ができるようになるのか
抽象的な話が続くと退屈なので、先に「できるようになること」を挙げます。すべて、実際の会社で日常的に発生している仕事です。
データを自動で取り込む
会計サービスや販売管理の売上を、毎朝5時に自動で自社の集計表に取り込む。
画面に外部の情報を出す
自社サイトに、天気・地図・為替・在庫といった外部サービスの情報を表示する。
通知を飛ばす
問い合わせが届いたら、SlackやLINE、メールに自動で通知する。
システム同士をつなぐ
受注システムに入った注文を、倉庫システムへ自動で流す(連携の中心的な仕事)。
AIを組み込む
文章の要約や分類を、AIサービスのAPIに任せて自社の業務に組み込む。
自社のAPIを作る
取引先に「うちのデータはここから取ってください」と窓口を提供する側になる。
学ぶ順番は「1本のリクエスト」を中心に据える
APIの学習でよくある遠回りは、いきなりライブラリやフレームワークの使い方から入る ことです。それだと、そのライブラリの外へ出た瞬間に何もできなくなります。順番はこうです。
HTTPの往復 を知る(STEP 1〜2)── すべてのWeb APIはこの上に乗っています
JSON を読み書きできる(STEP 3)── 返ってくるデータの形はほぼこれです
ヘッダー・ステータスコード・認証 を知る(STEP 4〜6)── 失敗の9割はここが原因です
curl で手で叩く(STEP 7)── プログラムを書く前に、必ず手で1回叩きます
プログラムに落とす (STEP 9〜10)── ここで初めてコードを書きます
この順番には理由があります。APIがうまく動かないとき、原因のほとんどはプログラムではなく「リクエストの中身」にある からです。手で叩ける人は、原因の場所を数分で切り分けられます。
手を動かす準備 このページのコマンドは、Windowsなら「コマンドプロンプト」か「PowerShell」(検索窓に cmd と入力)、Macなら「ターミナル」(アプリケーション → ユーティリティ)で実行します。curl は最近のWindows・Mac・Linuxには最初から入っています。Pythonの例を動かす場合はPythonの導入が必要ですが、STEP 9まではブラウザとcurlだけで進められます。
最初の一歩:本当にAPIを1回叩いてみる コピー
# 使える道具があるか確認する
curl --version
# 誰でも使える練習用のAPIに、いま日本は何時かを聞いてみる
curl "https://worldtimeapi.org/api/timezone/Asia/Tokyo"
返ってくるもの(例・一部)
{"abbreviation":"JST","datetime":"2026-08-14T09:12:34.567890+09:00",
"day_of_week":5,"timezone":"Asia/Tokyo","utc_offset":"+09:00", ...}
ぐしゃっとした文字列が出てくれば大成功です。あなたはいま、世界のどこかにあるサーバーに話しかけ、返事をもらいました 。これがAPI連携のすべての土台です。この読みにくい文字列を「読める形」にするのがSTEP 3です。
この分野の「略語の多さ」との付き合い方
APIの世界も、英語の頭文字を取った略語(HTTP、REST、JSON、OAuth ……)が非常に多い分野です。ここで挫折する人が多いのですが、対処法ははっきりしています。略語を覚えようとせず、「何をする役目か」を一言で覚える ことです。
この表は、ステップを進めるたびに戻ってきてください。10個の役目が言えるようになれば、API連携の会話の8割はついていけます。
ふりがな機能について このページは、難しい漢字や英語の略語にふりがな を付けています。画面上部の「ふりがな ON/OFF」ボタンでいつでも切り替えられ、設定は次に開いたときも引き継がれます。読み方を間違えて覚えると、会話で通じない・検索できないという実害が出ます(OAuth を「オーオース」ではなく別の読み方で覚えていて話が通じない、というのは実際によくあります)。最初は必ずONで読んでください 。
一問一答
APIとは何ですか。一言で答えてください。
APIを使う側は、相手の中身(データベースの構造など)を知る必要がありますか。それはなぜですか。
プログラムを書く前に、まずcurlで手で叩くとよいのはなぜですか。
解答を見る
サービスが公開している、プログラム向けの受付窓口(決まった形で頼めば、決まった形で返してくれる約束ごと)。
必要ない。APIが中身を隠し、決まった入口と出口だけを見せているため(カプセル化)。相手が内部を作り替えても、窓口の形が同じなら使う側は影響を受けない。
うまくいかない原因のほとんどは、プログラムではなくリクエストの中身(URL・ヘッダー・認証情報)にあるため。手で叩けると切り分けが数分で済む。
このステップは理解できた
STEP 1
基礎
HTTPの往復 ── リクエストとレスポンスがすべての土台
目安 3日
このステップの到達点 ── 「リクエスト」と「レスポンス」が、それぞれ3つの部品 でできていることを言える。そして、自分が送った1本のリクエストを、部品ごとに分解して説明できる。実務では、この分解ができるだけで障害対応の速さが変わります。
Web APIは「1往復」しかしない
Web APIの通信は、驚くほど単純です。あなたが1回頼み(リクエスト)、相手が1回返す(レスポンス)。それで1回分は終わり です。相手のサーバーは、あなたのことを次の瞬間には忘れています。この「毎回まっさらから始まる」性質をステートレス (状態を持たない)と呼びます。
だからこそ、毎回のリクエストに「自分が誰か」を書いて送る必要がある のです。ここが分かっていないと、STEP 6の認証で必ずつまずきます。「1回ログインしたのに、なぜ毎回トークンを付けるのか」の答えが、このステートレスという性質です。
1往復の中身
【リクエスト】あなた → 相手
① 開始行
GET /v1/users/12 HTTP/1.1
② ヘッダー(付帯情報)
Host: api.example.com
Authorization: Bearer …
③ ボディ(本文・任意)
{"name":"山田太郎"}
【レスポンス】相手 → あなた
① ステータス行
HTTP/1.1 200 OK
② ヘッダー(付帯情報)
Content-Type: application/json
X-RateLimit-Remaining: 58
③ ボディ(本文)
{"id":12,"name":"山田太郎"}
① どこに何をしたいか ② どんな条件・資格で ③ 実際のデータ
3つの部品の役割を、はっきり分ける
初学者が最も多く踏む地雷はヘッダーの書き忘れ です。「コードは合っているのに401が返る」の9割は、認証ヘッダーの付け方かデータ形式の指定ミスです。
実際に、往復のすべてを目で見る
curl -v(verbose = 詳しく)を付けると、送った内容と返ってきた内容がすべて画面に出ます 。これはAPI学習で最も効く道具のひとつです。
往復の全部を見る コピー
# -v で通信の中身を表示(> が送った内容、< が返ってきた内容)
curl -v "https://httpbin.org/get"
# ヘッダーだけを見たいとき(-I は HEAD リクエスト)
curl -I "https://httpbin.org/get"
画面に出るもの(抜粋)
> GET /get HTTP/1.1 ← ここから下が「あなたが送った」内容
> Host: httpbin.org
> User-Agent: curl/8.4.0
> Accept: */*
>
< HTTP/1.1 200 OK ← ここから下が「相手が返した」内容
< Content-Type: application/json
< Content-Length: 258
<
{ "args": {}, "headers": { ... }, "url": "https://httpbin.org/get" }
> が送信、< が受信です。この画面を読めるようになることが、STEP 1の目的です 。うまくいかないときは、まずこれを出して「自分が本当は何を送っていたのか」を確かめます。
練習に使える公開API httpbin.org は、送った内容をそのまま返してくれる練習専用のサービスです。自分が何を送ったかを確認する鏡 として使えます。ほかに jsonplaceholder.typicode.com(架空の投稿データ)もよく使われます。どちらも認証なしで叩けます。
APIサーバー・クライアント・エンドポイント
用語を3つだけ整理します。ここは実務の会話でそのまま出ます。
クライアント :頼む側。あなたのプログラム、ブラウザ、curl、スマホアプリなど
サーバー :返す側。相手のサービス
エンドポイント :頼み先の具体的なURL。https://api.example.com/v1/users のように、1つの機能につき1つ 用意されている
APIドキュメントを開くと、まず「エンドポイント一覧」が載っています。それは「この会社に頼めることの一覧」 だと思ってください。
やってはいけないこと 練習だからといって、他人のAPIに短い間隔で大量のリクエストを送らない でください。相手のサーバーに負荷をかける行為で、攻撃とみなされることがあります。繰り返し試すときは1秒以上の間隔を空け、公開APIの利用規約(利用条件)に必ず目を通してください。
一問一答
リクエストを構成する3つの部品を挙げてください。
HTTPが「ステートレス」であるとは、どういう意味ですか。それが認証にどう影響しますか。
「コードは正しいのに401が返る」とき、まずどこを疑いますか。
解答を見る
開始行(メソッド+パス)、ヘッダー、ボディ。
サーバーは前回の通信を覚えていないという意味。そのため、毎回のリクエストに認証情報(トークンなど)を付けて送る必要がある。
ヘッダー。Authorization の書式(Bearerの有無、余分な空白や改行)と、鍵の有効期限・環境(本番/検証)の取り違え。
このステップは理解できた
STEP 2
基礎
URLとメソッド ── 「どこに」「何をするか」を書く
目安 2〜3日
このステップの到達点 ── 初めて見るURLを部品ごとに分解でき、パスパラメータとクエリパラメータの違い を説明できる。GET・POST・PUT・PATCH・DELETEを、意味を分かって選べる。
URLを部品に分解する
APIドキュメントに書かれたURLは、必ず同じ構造をしています。一度覚えれば、どの会社のAPIでも同じように読めます。
URLの構造
https://api.example.com/v1/users/12?fields=name,email&limit=20
クエリパラメータ(条件・絞り込み)
パスパラメータ(どの1件か=ID)
リソース名(何を扱うか。ふつう複数形)
バージョン(v1・v2。仕様変更に備えた区切り)
ホスト名(どのサーバーか)
スキーム(通信方式。APIは必ず https)
パスパラメータとクエリパラメータの使い分け
ここは初学者が混乱する場所ですが、判断基準は明快です。
覚え方は「パスは住所、クエリは注文の細かい要望 」です。「12番の社員を見せて」は住所(/users/12)、「在籍中の人を20件だけ、名前順で」は要望(?status=active&limit=20&sort=name)です。
クエリパラメータの書き方の決まり 最初のひとつは ?、2つめ以降は & でつなぎます。値に日本語・空白・記号が入る場合はURLエンコード (パーセントエンコーディング)が必要です。「東京」は %E6%9D%B1%E4%BA%AC、空白は %20 になります。curlなら --data-urlencode、プログラムなら各言語の標準機能が自動で処理してくれます。手で書かず、道具に任せる のが鉄則です。
HTTPメソッド ── 「何をするか」を1語で伝える
メソッド は、同じURLに対して「読むのか、作るのか、消すのか」を示す動詞です。URLは名詞、メソッドは動詞 と覚えてください。
冪等性 ── 実務でいちばん大事な性質
冪等性 (べきとうせい/idempotency)とは、同じ操作を何回繰り返しても、結果が1回のときと同じになる性質 のことです。読み方を間違えやすい言葉ですが、意味は難しくありません。
GET /users/12 を100回叩いても、データは何も変わらない → 冪等
DELETE /users/12 を100回叩いても、消えている状態は同じ → 冪等
POST /orders を100回叩くと、注文が100件できる → 冪等ではない
この違いが効くのは、通信が失敗して、もう一度送るべきか迷ったとき です。GETやPUTなら安心して再送できますが、POSTの再送は二重注文・二重請求を生みます 。だから支払いや注文のAPIには、Idempotency-Key という「同じ鍵の依頼は1回しか受け付けない」ヘッダーが用意されていることが多いのです(STEP 11で扱います)。
実務でのやらかし 「送信ボタンを押したが反応がないので、もう一度押した」──これで二重注文が発生する事故は、いまも毎日どこかで起きています。POSTを扱うときは、再送されうる前提で設計する のが実務の常識です。
メソッドを変えて試す コピー
# 取得(GET)。-X は省略してよい(curlの既定がGETのため)
curl "https://jsonplaceholder.typicode.com/posts/1"
# 一覧を条件つきで取得(クエリパラメータ)
curl "https://jsonplaceholder.typicode.com/posts?userId=1&_limit=3"
# 作成(POST)。-d でボディを送り、形式をヘッダーで伝える
curl -X POST "https://jsonplaceholder.typicode.com/posts" \
-H "Content-Type: application/json" \
-d '{"title":"はじめてのAPI","body":"テスト投稿","userId":1}'
# 削除(DELETE)
curl -X DELETE "https://jsonplaceholder.typicode.com/posts/1"
POSTの結果(例)
{
"title": "はじめてのAPI",
"body": "テスト投稿",
"userId": 1,
"id": 101
}
送っていない id が付いて返ってきました。採番は相手のサーバーの仕事 だからです。「作成したものの結果を返す」のはREST APIの標準的なふるまいで、この id を控えておくと、あとで更新や削除ができます。
一問一答
/products/88?color=red のうち、パスパラメータとクエリパラメータはそれぞれどれですか。
冪等性とは何ですか。POSTは冪等ですか。
「更新」にPUTとPATCHのどちらを使うべきか、判断基準を述べてください。
解答を見る
パスパラメータは 88(どの商品か)、クエリパラメータは color=red(絞り込み条件)。
同じ操作を何回繰り返しても結果が1回と同じになる性質。POSTは冪等ではない(叩いた回数だけ作られる)。
送らなかった項目まで空にしてよいならPUT(まるごと置き換え)、送った項目だけを変えたいならPATCH(部分更新)。実務ではPATCHのほうが事故が少ない。
このステップは理解できた
STEP 3
基礎
JSON ── 返ってくるデータを読み書きする
目安 3日
このステップの到達点 ── JSONを見て、ほしい値までの道すじ(パス)を口で言える 。オブジェクトと配列を見分けられ、書式の誤りを自分で直せる。ここができると、APIの返事が「読める文書」に変わります。
JSONは、たった6種類しかない
JSON (ジェイソン)は、データを文字で表すための書式です。覚えるのは6種類だけです。
よくある書式エラーは3つです。①末尾のカンマ (最後の項目のうしろにカンマを付けてしまう)、②シングルクォート 、③コメントを書いてしまう 。JSONにコメントは書けません。
入れ子(ネスト)を、道すじとして読む
実際のAPIの返事は、オブジェクトの中に配列、配列の中にオブジェクト……と入れ子になっています。読み方のこつは、上から順に「〜の中の〜の中の」と声に出す ことです。
よくある形のレスポンス コピー
{
"status": "ok",
"total": 2,
"users": [
{
"id": 12,
"name": "山田太郎",
"active": true,
"department": { "code": "S01", "name": "営業部" },
"tags": ["新規", "重点"]
},
{
"id": 13,
"name": "鈴木花子",
"active": false,
"department": { "code": "K02", "name": "経理部" },
"tags": []
}
]
}
道すじの読み方
山田太郎の所属部署名がほしい
→ users の 0番目 の department の name
→ users[0].department.name = "営業部"
2人目の1つ目のタグは?
→ users[1].tags[0] = 存在しない(空の配列)→ ここで落ちるコードが多い
配列の番号は0から始まります 。1件目は [0] です。ここを間違えると、実務でも「1件ずれる」不具合の温床になります。
実務でいちばん多いバグ 「あるはずの項目が無い」 ことによるエラーです。上の例の tags のように空だったり、項目そのものが返らなかったり、null だったりします。APIの返事は、いつも同じ形とは限りません 。STEP 11で、この防ぎ方を扱います。
読める形に整える(整形とjq)
curlの結果は1行にぎゅっと詰まっていて読めません。整形する方法を覚えておくと、以降の学習が一気に楽になります。
JSONを読める形にする コピー
# Python が入っていれば、追加インストール不要で整形できる
curl -s "https://jsonplaceholder.typicode.com/users/1" | python3 -m json.tool
# jq を使う場合(別途インストール。API業務では定番の道具)
curl -s "https://jsonplaceholder.typicode.com/users/1" | jq
# ほしい値だけ取り出す(jq)
curl -s "https://jsonplaceholder.typicode.com/users/1" | jq '.address.city'
# 一覧から、名前だけを並べる
curl -s "https://jsonplaceholder.typicode.com/users" | jq '.[].name'
jq の結果(例)
"Gwenborough"
"Leanne Graham"
"Ervin Howell"
"Clementine Bauch"
…
-s は進捗表示を消すオプションです。jq の書き方は JSONの道すじをそのまま書くだけ (.users[0].name)なので、上で練習した読み方がそのまま使えます。
ブラウザでも整形できます Chromeなら、APIのURLをそのままアドレス欄に貼れば結果が見られます(認証不要のAPIに限る)。開発者ツール(F12キー)の「ネットワーク」タブを開くと、いま見ているサイトが裏で叩いているAPI も全部見えます。これは学習教材の宝庫です。
日付・数値・文字コードの落とし穴
日付 :"2026-08-14T09:12:34+09:00" という書式(ISO 8601)が標準です。末尾の Z は世界標準時(UTC)を意味し、日本時間より9時間前 です。ここを取り違えると「日付が1日ずれる」不具合になります。
数値 :金額やIDは、文字列で返ってくることがあります ("1200")。桁が大きいIDは、数値として扱うと精度が落ちるためです。ドキュメントの型を必ず確認してください。
文字コード :JSONの文字コードはUTF-8 が基本です。日本語が「譁�蟄怜喧縺�」のように化けたら、まず文字コードの指定を疑います。
一問一答
JSONで文字列を囲むとき、シングルクォートは使えますか。
上の例で「鈴木花子の部署コード」を取り出す道すじを書いてください。
null、空文字 ""、0 の違いを説明してください。
解答を見る
使えない。JSONの文字列は必ずダブルクォートで囲む。
users[1].department.code(= "K02")。配列は0から数えるため、2人目は[1]。
nullは「値が無い・未設定」、""は「空という値がある」、0は「ゼロという数値がある」。たとえば在庫が null は「未調査」、0 は「品切れ」を意味しうる、まったく別の情報。
このステップは理解できた
STEP 4
しくみ
ステータスコードとヘッダー ── 失敗の9割はここで分かる
目安 3日
このステップの到達点 ── 返ってきた3桁の数字を見て、「悪いのは自分か相手か」を即答できる 。主要なヘッダーの役割を言え、Content-Type の指定ミスを自力で直せる。実務の切り分けは、ほぼこの2つで決まります。
ステータスコードは「百の位」で意味が決まる
細かい番号を暗記する必要はありません。百の位だけで、責任の所在が分かります 。
4xxを見て相手に問い合わせるのは恥ずかしい 、と覚えてください。4xxは「あなたの依頼書の書き方が違う」という意味です。逆に5xxが続くなら、こちらでいくら直しても直りません。この一線を引けることが、実務での信頼につながります 。
これだけは覚える12個
401と403の違いは、面接でもよく聞かれます 401=「名乗ってください」 (認証の問題)、403=「あなたには見せられません」 (認可の問題)。401は鍵を直せば通りますが、403は鍵を何度直しても通りません 。管理者に権限を付けてもらう必要があります。ここを取り違えると、何時間も無駄にします。
覚えておくべきヘッダー
X- で始まるものは、各社が独自に定めたヘッダーです。X-Request-Id は必ずログに残してください 。相手のサポートに問い合わせるとき、この番号があるかないかで解決までの時間が何倍も変わります。
ステータスコードとヘッダーを実際に見る コピー
# わざと404を返してもらう
curl -i "https://httpbin.org/status/404"
# ステータスコードだけを数字で取り出す(バッチ処理の判定でよく使う書き方)
curl -s -o /dev/null -w "%{http_code}\n" "https://httpbin.org/status/500"
# 自分が送ったヘッダーを、そのまま見せてもらう
curl -s "https://httpbin.org/headers" -H "X-My-Header: test123"
# 応答にかかった時間も測る
curl -s -o /dev/null -w "code=%{http_code} time=%{time_total}s\n" "https://httpbin.org/delay/2"
結果(例)
HTTP/1.1 404 NOT FOUND
Content-Type: text/html; charset=utf-8
Content-Length: 0
500
code=200 time=2.213145s
-i はヘッダーも一緒に表示するオプション、-w は好きな情報を書き出すオプションです。-o /dev/null -w "%{http_code}" の書き方は、実務のシェルスクリプトで頻出 なので覚えておくと役立ちます。
200が返っても成功とは限りません 一部のAPIは、エラーでも200を返し、ボディの中に {"result":"error","message":"..."} と書いてきます。ステータスコードとボディの両方を確認する のが安全です。特に国内の古いシステムや、SOAP由来のAPIでよく見られます。
一問一答
401と403の違いを説明してください。
5xxが返り続けるとき、自分のコードを直せば解決しますか。
429が返ったとき、まず確認すべきヘッダーは何ですか。
解答を見る
401は「あなたが誰か分からない」=認証の失敗。403は「誰かは分かるが権限がない」=認可の失敗。403は鍵を直しても通らず、権限の付与が必要。
解決しない。5xxは相手のサーバー内部の問題。時間を置いて再送し、続くようなら送った内容とX-Request-Idを添えて相手に連絡する。
Retry-After(何秒待つべきか)。あわせてX-RateLimit-*系で残り回数と上限も確認する。
このステップは理解できた
STEP 5
しくみ
REST API ── いま最も普及している作法
目安 3日
このステップの到達点 ── RESTの考え方(リソース中心)を説明でき、エンドポイント一覧を見ただけで「これは何ができるAPIか」を推測できる 。RESTでないAPI(SOAP・GraphQL・gRPC)との違いも一言で言える。
RESTとは「データを名詞として扱う」考え方
REST (レスト)は、規格ではなく設計の作法 です。中心にある考え方はひとつだけ、「操作対象(リソース)をURLで表し、操作の種類はHTTPメソッドで表す」 ことです。
作法に沿っていないAPIは、こう書かれます。
悪い例と良い例
✕ 動詞をURLに入れる(何でもPOST)
POST /getUserInfo
POST /createUser
POST /deleteUser
URLを見ても何ができるか分からない。
窓口の数が、機能の数だけ無限に増える。
○ 名詞+メソッド(RESTの作法)
GET
/users
一覧を取る
GET
/users/12
1件取る
POST
/users
作る
PATCH
/users/12
一部を直す
DELETE
/users/12
消す
5つの型を覚えれば、どのリソースも同じように扱える。
RESTらしいURLの作り方(自社APIを作る側になったときも同じ)
リソース名は複数形の名詞 :/users、/orders、/invoices
階層は「親/子」で表す :/users/12/orders(12番の利用者の注文一覧)
深い階層は2段まで :3段以上は分かりにくくなる。/orders?user_id=12 のほうがよい場合が多い
絞り込み・並び替え・ページは、クエリで :?status=paid&sort=-created_at&page=2
バージョンを先頭に置く :/v1/users。仕様変更のとき、古い利用者を壊さずに済む
どうしても動詞が必要なとき 「送信する」「取り消す」「再計算する」のような、CRUDに収まらない操作はあります。その場合は、その操作を1つのリソースとみなす のが定石です。POST /orders/12/cancellation や POST /orders/12/actions/cancel のように書きます。原理主義に陥る必要はありません。チーム内で一貫していることのほうが大事 です。
典型的なエンドポイント設計を、表で覚える
REST以外の方式も、名前だけは知っておく
日本の企業システムでは、いまもSOAP・XML・固定長ファイル の連携が現役です。RESTだけが連携ではない、と知っておくと、現場で驚かずに済みます。
同じリソースを、メソッドを変えて一通り触る コピー
# 一覧(絞り込みつき)
curl -s "https://jsonplaceholder.typicode.com/posts?userId=1&_limit=2"
# 1件取得
curl -s "https://jsonplaceholder.typicode.com/posts/1"
# 一部更新(PATCH)
curl -s -X PATCH "https://jsonplaceholder.typicode.com/posts/1" \
-H "Content-Type: application/json" \
-d '{"title":"題名だけ差し替え"}'
# 親子の関係(1番の投稿に付いたコメント一覧)
curl -s "https://jsonplaceholder.typicode.com/posts/1/comments?_limit=2"
一問一答
RESTの中心にある考え方を一言で述べてください。
POST /deleteUser?id=12 という設計の問題点を2つ挙げてください。
POSTで作成が成功したとき、返るべきステータスコードと、確認すべきヘッダーは何ですか。
解答を見る
操作対象(リソース)をURLで表し、操作の種類はHTTPメソッドで表す。
①URLに動詞が入っており、エンドポイントが無限に増える。②削除なのにPOSTのため、意味(冪等性・安全性)がHTTPの規約と食い違い、途中の機器やライブラリの前提が崩れる。正しくは DELETE /users/12。
201 Created。Location ヘッダーに、作成されたリソースのURLが入る。
このステップは理解できた
STEP 6
しくみ
認証と認可 ── APIキー・Bearerトークン・OAuth 2.0
目安 5日(最重要)
このステップの到達点 ── 認証と認可の違いを説明でき、APIキー方式とOAuth方式を、ドキュメントを見て区別できる 。アクセストークンとリフレッシュトークンの役割を言え、期限切れの扱いを設計できる。ここが実務でいちばん詰まる場所 です。
認証(Authentication)と認可(Authorization)
英語だとどちらも「Auth」で始まるため、略して AuthN(認証)/AuthZ(認可) と書き分けることがあります。ドキュメントで見かけたら思い出してください。
方式は、実務では4つに絞られる
Base64は暗号ではありません Basic認証の文字列は、誰でも一瞬で元に戻せます。「暗号化されているから安全」という説明を聞いたら、それは誤りです。安全性を担保しているのはHTTPS(通信路の暗号化) のほうです。
OAuth 2.0を、登場人物で理解する
OAuth 2.0 (オーオース ツーテンゼロ)は、「パスワードを渡さずに、必要な権限だけを他社アプリに貸す」 ためのしくみです。「〇〇でログイン」ボタンの裏側は、たいていこれです。
OAuth 2.0 の登場人物と流れ(認可コード方式)
① 利用者
サービスの使い手
② あなたのアプリ
クライアント
③ 認可サーバー
ログイン画面
④ リソースサーバー
データを持つAPI
① 利用者を③の画面へ送る
② 利用者が「許可します」を押す
③ 認可コードを渡す
④ 認可コード+自分の秘密を送る
⑤ アクセストークンを発行
⑥ Bearerトークンを付けて叩く
⑦ データが返る
★ ②(あなたのアプリ)は、利用者のパスワードを一度も受け取りません。だから安全で、利用者はいつでも許可を取り消せます。
ここで最も大事な点は、あなたのアプリは利用者のパスワードを一度も受け取らない ことです。だから安全ですし、利用者はいつでも許可を取り消せます。「なぜOAuthが要るのか」と聞かれたら、この一点を答えてください 。
アクセストークンとリフレッシュトークン
実装のときは、「401が返ったらリフレッシュして1回だけ再試行する」 という定石をそのまま実装します。ここを毎回書くのは大変なので、公式SDKがある場合はSDKに任せるのが賢明です。
認証つきでAPIを叩く(4方式) コピー
# ① APIキー(ヘッダーで送る。推奨)
curl -s "https://api.example.com/v1/users" \
-H "X-API-Key: $API_KEY"
# ② Basic認証(curlが自動でBase64にしてくれる)
curl -s -u "myuser:mypassword" "https://api.example.com/v1/users"
# ③ Bearerトークン(いま最も多い形)
curl -s "https://api.example.com/v1/users" \
-H "Authorization: Bearer $ACCESS_TOKEN"
# ④ OAuth 2.0 でトークンを取る(クライアントクレデンシャル方式=機械同士の連携)
curl -s -X POST "https://api.example.com/oauth/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "client_id=$CLIENT_ID" \
-d "client_secret=$CLIENT_SECRET"
④の返り(例)
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "users.read orders.read"
}
expires_in は秒数 です。3600なら1時間で切れます。受け取った時刻+expires_in を控えておき、期限前に作り直すのが定石です。scope は「この鍵でできること」の一覧で、認可の範囲を表します。
環境変数で鍵を扱う 上の例で $API_KEY と書いているのは、鍵をコマンドに直接書かない ためです。export API_KEY="abc123"(Windows PowerShellなら $env:API_KEY="abc123")としてから実行します。直接書くと、シェルの履歴ファイルに平文で残ります。詳しくはSTEP 15で扱います。
JWT(ジェイダブリューティー)について Bearerトークンの中身が eyJ... で始まっていたら、それはJWT (JSON Web Token)です。ピリオドで3つに区切られ、真ん中に利用者IDや有効期限がJSONで入っています 。Base64なので誰でも中身を読めます (改ざんは署名で防いでいるだけ)。JWTに個人情報やパスワードを入れてはいけません 。中身は jwt.io のようなサイトで確認できますが、本番のトークンを他人のサイトに貼らないでください 。
一問一答
認証と認可の違いを、返るステータスコードとあわせて説明してください。
OAuth 2.0を使う最大の利点は何ですか。
リフレッシュトークンの役割と、扱い上の注意を述べてください。
解答を見る
認証は「誰か」を確かめること(失敗は401)、認可は「何をしてよいか」を決めること(失敗は403)。
利用者のパスワードを、連携するアプリ側に渡さずに済むこと。権限の範囲(scope)を限定でき、利用者がいつでも許可を取り消せる。
期限の短いアクセストークンを作り直すための引換券。長期間有効なため最重要機密として扱い、ログや画面に出さず、安全な場所(環境変数・秘密管理サービス)に保管する。
このステップは理解できた
STEP 7
しくみ
curl ── プログラムを書く前に、手で叩けるようになる
目安 3日
このステップの到達点 ── curlの主要オプション10個を使い分けられ、ドキュメントのサンプルを自分の環境用に書き換えて実行できる 。ここまで来ると、「動かない」ときに自分ひとりで原因までたどり着けます。
なぜ、まずcurlなのか
プログラムからAPIを呼んで失敗したとき、原因の候補は「リクエストの中身」「プログラムの書き方」「相手の状態」 の3つあります。curlで手で叩いて成功すれば、原因はプログラム側だと確定します 。逆にcurlでも失敗すれば、プログラムをいくら直しても無駄だと分かります。この切り分けができるかどうかが、初学者と実務者の分かれ目 です。
覚えるべきオプション10個
実務でそのまま使える形(雛形) コピー
# 【雛形】これをコピーして、URLとヘッダーだけ差し替える
curl -s -i --max-time 30 \
-X POST "https://api.example.com/v1/orders" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{"product_id":"A-100","quantity":2}'
# 長いJSONは、ファイルに書いて渡すほうが確実(@はファイル指定の意味)
curl -s -X POST "https://api.example.com/v1/orders" \
-H "Content-Type: application/json" \
-d @order.json
# クエリに日本語や記号が入るときは、curlに変換させる(-G はGETで送る指定)
curl -s -G "https://api.example.com/v1/search" \
--data-urlencode "q=東京 支店" \
--data-urlencode "limit=10"
Windowsのコマンドプロンプトでの落とし穴 Windowsの cmd では、シングルクォート(')が使えません 。JSONを送るときはダブルクォートを使い、中のダブルクォートを \" と書く必要があります(例:-d "{\"id\":1}")。面倒なので、WindowsではPowerShellを使うか、JSONをファイルにして -d @file.json で渡す のが実務上の解決策です。改行を続ける記号も、cmdは ^、PowerShellは `(バッククォート)で、Mac/Linuxの \ とは違います。
うまくいかないときの手順(この順番で)
1. -v を付ける まず curl -v で自分が実際に何を送ったか を見る。ここで解決することが非常に多い
2. コードを見る 4xxなら自分、5xxなら相手 (STEP 4)。ここで調べる方向が決まる
3. ボディを読む エラーの本文に理由が書いてある。{"error":{"message":"..."}} を必ず読む
4. ヘッダーを疑う Authorization の綴り、Bearer の後の半角空白、鍵の前後の改行や空白
5. URLを疑う 末尾のスラッシュ、バージョン(v1/v2)、本番と検証(sandbox)の取り違え
6. 通信の外側を疑う 社内プロキシ、ファイアウォール、IP制限(相手側の許可リストに自社IPが入っているか)
切り分けに使う小技 コピー
# 通信の詳細だけを見て、本文は捨てる
curl -v -o /dev/null "https://api.example.com/v1/ping"
# 応答が遅いのか、処理が遅いのかを測る
curl -s -o /dev/null -w "接続:%{time_connect}s 初回応答:%{time_starttransfer}s 合計:%{time_total}s\n" \
"https://api.example.com/v1/orders"
# 証明書の問題を確認する(-k は検証を無視。原因調査のときだけ使う)
curl -v -k "https://api.example.com/v1/ping"
# プロキシ越しに叩く(社内ネットワークでよく必要になる)
curl -s -x "http://proxy.example.co.jp:8080" "https://api.example.com/v1/ping"
-k を本番で使ってはいけません -k(--insecure)は、通信相手が本物かどうかの確認を飛ばすオプションです。原因調査で「証明書が原因か」を確かめるときだけ使い、本番のプログラムには絶対に残さないでください 。中間者攻撃(通信を横取りされる攻撃)を防げなくなります。
ブラウザからcurlコマンドを作れます Chromeの開発者ツール(F12)→「ネットワーク」タブ→対象の通信を右クリック→「Copy as cURL」 。いま見ている画面が裏で叩いているリクエストが、そのまま実行できるcurlコマンドとして手に入ります 。他社APIの挙動を調べるときの、最も実用的な技のひとつです。
一問一答
プログラムでAPI呼び出しが失敗したとき、curlで叩いてみると何が分かりますか。
-v と -i の違いを説明してください。
本番のバッチ処理で --max-time を必ず付けるべき理由は何ですか。
解答を見る
原因がリクエストの中身にあるのか、プログラムの書き方にあるのかを切り分けられる。curlで成功すればプログラム側、curlでも失敗すればリクエストか相手側の問題。
-iは「返ってきたヘッダー+本文」を表示する。-vは「送った内容と返ってきた内容の両方」を通信の詳細まで表示する。原因調査では-v。
相手が応答を返さないとき、処理が永久に止まってしまうため。上限を決めておかないと、バッチが翌日まで終わらない、後続処理が全部止まる、といった事故になる。
このステップは理解できた
STEP 8
しくみ
APIドキュメントの読み方 ── OpenAPIとPostman
目安 3日
このステップの到達点 ── 初めて見るAPIドキュメントを、決まった順番で読んで15分で1回目の呼び出しにたどり着ける 。OpenAPI(Swagger)の画面から必要な情報を拾え、Postmanで試験と共有ができる。
ドキュメントは、この順番で読む
どの会社のAPIドキュメントも、載っている項目はほぼ同じです。読む順番を固定しておくと、初見でも迷いません 。
1. 認証 「Authentication」の節。ここを最初に読む 。鍵の取り方と、送り方(ヘッダー名)を確認
2. 基点URL 「Base URL」。本番と検証(sandbox / staging)でURLが違う ことに注意
3. クイックスタート 「Getting Started」の最初のサンプルをそのままコピーして実行 。まず1回成功させる
4. エンドポイント一覧 「API Reference」。目的の機能を探す。目次だけ眺めて全体像をつかむ
5. 制限 「Rate Limits」。1分あたり何回まで叩けるか。ここを読まずに作ると本番で止まる
6. エラー 「Errors」。返るコードの一覧と、独自エラーコードの意味
7. 変更履歴 「Changelog」。いつ仕様が変わるか 。廃止予定(deprecated)の項目に注意
まず1回成功させることを最優先に 全部を読んでから作り始めるのは、遠回りです。クイックスタートの1本を成功させてから 、必要な部分だけ読み進めてください。1本通っていれば、認証・URL・形式がすべて正しいと確定しているので、あとは中身を変えるだけになります。
OpenAPI(Swagger)── 機械が読める仕様書
OpenAPI (オープンエーピーアイ/旧称Swagger)は、APIの仕様をYAMLやJSONで書いた設計図 です。これがあると、次のことが自動でできます。
ブラウザでそのまま試せる画面 (Swagger UI)が生成される
各言語の呼び出しコードが自動生成 できる
Postmanなどの道具にまるごと読み込める
openapi.yaml(読み方のポイントだけ) コピー
openapi: 3.0.3
info:
title: 受注管理API # このAPIの名前
version: "1.0.0"
servers:
- url: https://api.example.com/v1 # ← 基点URL(本番)
- url: https://sandbox.example.com/v1 # ← 検証環境
paths:
/orders: # ← エンドポイント
get: # ← メソッド
summary: 注文の一覧を取得する
parameters:
- name: status
in: query # ← クエリパラメータ(in: path ならパス)
required: false
schema: { type: string, enum: [pending, paid, shipped] }
responses:
"200":
description: 成功
content:
application/json:
schema:
type: array
items: { $ref: "#/components/schemas/Order" }
"401": { description: 認証エラー }
components:
schemas:
Order:
type: object
required: [id, status] # ← 必ず入っている項目
properties:
id: { type: integer, example: 1001 }
status: { type: string, example: "paid" }
amount: { type: number, example: 12000 }
note: { type: string, nullable: true } # ← null が来る可能性あり
ここだけ見れば実装できる
① servers → どこに送るか
② paths のキー → URL(/orders)
③ その下のメソッド → get / post / patch …
④ parameters → 送れる条件(in: query か in: path か)
⑤ requestBody → 送るJSONの形(POST・PATCHのとき)
⑥ responses の 200 → 返ってくるJSONの形
⑦ required / nullable → 必須項目と、nullが来る項目 ★実装で最重要
required に無い項目は、返ってこないことがあります 。nullable: true の項目は null が入ります。この2つを見落とすと、STEP 11で扱う「項目が無くて落ちる」不具合になります。
Postman ── 試して、記録して、共有する
Postman (ポストマン)は、APIを画面上で試すための道具です。curlと同じことができますが、チームで共有できる点 が実務では決定的に便利です。
Postmanで気をつけること ①本番の鍵をコレクションに直接書いて共有しない (環境変数に入れ、共有対象から外す)。②クラウド同期をONにすると、入力した鍵が社外のサーバーに保存されます 。会社の規程を必ず確認してください。③無料の「オンラインAPIテストサイト」に本番データを貼るのも同じ理由で危険です。
ドキュメントが無い・古いとき(実務ではよくある)
社内システムや古い連携先では、ドキュメントが存在しない、あっても実物と食い違うことが日常的にあります。そのときの手順です。
まず実物を叩いて、返ってきたJSONを保存する 。それが最も正しい仕様書になります
ブラウザの開発者ツールで、既存画面が叩いている通信を観察する
相手の担当者に聞くときは、「叩いたURL・送った内容・返ってきたコードと本文・X-Request-Id」 をそろえて聞く。これがあるかないかで、返答の速さがまるで違います
分かったことは、自分でOpenAPIかMarkdownに書き起こして残す 。次の担当者を救い、自分の評価にもつながります
一問一答
初めてのAPIドキュメントで、最初に読むべき節はどこですか。
OpenAPIの required と nullable は、実装のとき何を意味しますか。
ドキュメントが実物と食い違っているとき、どちらを正としますか。
解答を見る
認証(Authentication)。鍵の取得方法と送り方が分からないと、何ひとつ叩けないため。次に基点URLとクイックスタート。
requiredに無い項目は返ってこない可能性があり、nullable: trueの項目はnullが来る可能性がある。どちらも、そのまま値を取り出すと落ちるため、存在確認や既定値の用意が必要。
実物(実際に返ってきたレスポンス)。ただし、実物が仕様外の異常動作である可能性もあるため、食い違いは必ず相手に確認し、記録に残す。
このステップは理解できた
STEP 9
実装
JavaScriptのfetch ── ブラウザからAPIを呼ぶ
目安 5日
このステップの到達点 ── fetch と async/await を使って、取得・作成・エラー処理まで書ける。「fetchは404でも例外を投げない」 という落とし穴を知り、正しく判定できる。CORSエラーの意味と、正しい対処を説明できる。
いちばん短い形
GET:データを取ってきて表示する コピー
// async を付けた関数の中では await が使える
async function getUser(id) {
const res = await fetch(`https://jsonplaceholder.typicode.com/users/${id}`);
// ★重要:fetch は 404 や 500 でも例外を投げない。自分で確かめる
if (!res.ok) {
throw new Error(`APIエラー: ${res.status} ${res.statusText}`);
}
const data = await res.json(); // JSON文字列を、扱える形に変換する
return data;
}
// 呼び出す側。失敗する前提で try/catch を必ず書く
async function main() {
try {
const user = await getUser(1);
console.log(user.name, user.address.city);
} catch (err) {
console.error("取得に失敗しました:", err.message);
}
}
main();
初学者が100%踏む落とし穴 fetch は、404や500が返ってきても「通信としては成功」とみなし、例外を投げません 。例外になるのは、ネットワークが切れている、URLが解決できない、CORSで拒否された、といったそもそも届かなかった場合だけ です。if (!res.ok) を書き忘れると、エラーのHTMLをJSONとして読もうとして、意味不明なエラーになります 。
POSTで送る
POST:JSONを送って作成する コピー
async function createPost(title, body) {
const res = await fetch("https://jsonplaceholder.typicode.com/posts", {
method: "POST",
headers: {
"Content-Type": "application/json", // 送る形式を伝える
"Accept": "application/json",
// "Authorization": `Bearer ${token}` // 認証が要るAPIではこれを足す
},
body: JSON.stringify({ title, body, userId: 1 }) // ★オブジェクトを文字列に変換
});
if (!res.ok) {
// エラーの本文にも、たいてい理由が書いてある。読まずに捨てない
const text = await res.text();
throw new Error(`${res.status}: ${text}`);
}
return await res.json();
}
忘れやすい点が2つ あります。①body には文字列 を渡す(JSON.stringify が必要)。②Content-Type を書かないと、相手が形式を判断できず400や415が返ります。この2つで、初学者のPOSTの失敗はほぼ説明がつきます 。
タイムアウトを付ける(実務では必須)
fetch には既定のタイムアウトがありません。相手が黙り込むと永久に待ち続けます 。AbortSignal で必ず上限を付けてください。
10秒で打ち切る コピー
async function fetchWithTimeout(url, options = {}, ms = 10000) {
const ctrl = new AbortController();
const timer = setTimeout(() => ctrl.abort(), ms);
try {
return await fetch(url, { ...options, signal: ctrl.signal });
} catch (err) {
if (err.name === "AbortError") {
throw new Error(`${ms}ミリ秒以内に応答がありませんでした`);
}
throw err;
} finally {
clearTimeout(timer); // 成功しても失敗しても、必ず後片付けする
}
}
// 新しい環境なら、これだけでも書ける
// const res = await fetch(url, { signal: AbortSignal.timeout(10000) });
CORS ── ブラウザ特有の壁
CORS (コルス)は、ブラウザが持っている安全装置 です。「Aというサイトで開いているページが、Bという別のサイトのAPIを勝手に呼ぶ」ことを、既定で禁止しています。これがないと、あなたが開いた悪意あるページが、あなたのログイン済み社内システムを裏で操作できてしまいます。
CORSで拒否されるしくみ
ブラウザ
mysite.example.jp を表示中
相手のサーバー
api.other.com
① OPTIONS(プリフライト)
② 許可ヘッダーを返す(返さない)
許可があれば通す
fetchの結果を受け取れる
無ければブラウザが遮断
サーバーには届いていることも
★ 遮断しているのは「ブラウザ」です。だからcurlやPythonでは成功するのに、ブラウザからだけ失敗します。
ブラウザのJavaScriptに、APIキーを書いてはいけません ページのソースは誰でも表示できます 。「難読化したから大丈夫」も通用しません。秘密の鍵が要るAPIは、必ずサーバー側から呼ぶ 。これはAPI連携の最重要の原則のひとつで、実際に多くの情報漏えい事故がここで起きています。
並行して取る 3つのAPIを順番に待つと、3回分の時間がかかります。互いに依存しないなら Promise.all でまとめて投げると、いちばん遅い1本の時間で済みます 。ただし相手のレート制限(STEP 13)に注意してください。一部失敗を許容したい場合は Promise.allSettled を使います。
一問一答
fetchは404のときに例外を投げますか。どう判定すべきですか。
POSTでJSONを送るとき、必ず必要な2つのことは何ですか。
curlでは成功するのにブラウザだけCORSエラーになるのは、なぜですか。
解答を見る
投げない。res.ok(またはres.status)を自分で確認して判定する。
①JSON.stringifyでボディを文字列にすること、②Content-Type: application/jsonヘッダーを付けること。
CORSはブラウザが持つ安全装置で、遮断しているのはブラウザだから。curlやサーバー側のプログラムにはこの仕組みがないため、そのまま通る。
このステップは理解できた
STEP 10
実装
フォームからAPIへ ── 入力・検証・送信・表示のひと続き
目安 5日
このステップの到達点 ── 画面のフォームから受け取った値を、検証してJSONに組み立て、APIに送り、結果を画面に返す までを一人で書ける。二重送信の防止と、利用者に伝わるエラー表示ができる。実務の依頼の半分は、この形 です。
フォーム送信の6段階
1. 既定動作を止める e.preventDefault()。これを忘れると画面が再読み込みされ、何も起きていないように見える
2. 値を取り出す new FormData(form) で、入力欄の値をまとめて受け取る
3. 検証する 必須・形式・桁数を送る前に 確かめる。サーバー側の検証も必須 (画面の検証は迂回できる)
4. ボタンを止める 送信中はボタンを無効化 。これが二重送信を防ぐ最も基本的な手当て
5. 送る fetchでPOST。タイムアウトを付ける
6. 結果を出す 成功・失敗のどちらも利用者の言葉で 表示し、ボタンを戻す
form.html:入力フォーム コピー
<form id="contactForm" novalidate>
<label for="name">お名前</label>
<input id="name" name="name" type="text" required maxlength="40">
<label for="email">メールアドレス</label>
<input id="email" name="email" type="email" required>
<label for="qty">数量</label>
<input id="qty" name="qty" type="number" min="1" max="99" value="1">
<label for="message">お問い合わせ内容</label>
<textarea id="message" name="message" rows="5" required></textarea>
<button type="submit" id="submitBtn">送信する</button>
<p id="formMsg" role="status" aria-live="polite"></p>
</form>
aria-live="polite" を付けておくと、送信結果が読み上げ環境でも伝わります 。label と id をひも付けるのも同じ理由で、実務では必ず求められます。
form.js:検証してAPIに送る コピー
const form = document.getElementById("contactForm");
const btn = document.getElementById("submitBtn");
const msg = document.getElementById("formMsg");
form.addEventListener("submit", async (e) => {
e.preventDefault(); // ① 画面の再読み込みを止める
const fd = new FormData(form); // ② 値をまとめて取り出す
const payload = {
name: String(fd.get("name") || "").trim(),
email: String(fd.get("email") || "").trim(),
qty: Number(fd.get("qty") || 1),
message: String(fd.get("message") || "").trim()
};
const error = validate(payload); // ③ 送る前に検証する
if (error) { show(error, "ng"); return; }
btn.disabled = true; // ④ 二重送信を防ぐ
show("送信しています…", "");
try { // ⑤ 送る
const res = await fetch("/api/contacts", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload),
signal: AbortSignal.timeout(15000)
});
if (res.status === 422 || res.status === 400) {
const err = await res.json(); // 相手が返した項目別エラーを使う
show(err.message || "入力内容をご確認ください。", "ng");
return;
}
if (!res.ok) throw new Error(`status ${res.status}`);
const data = await res.json(); // ⑥ 結果を出す
show(`受け付けました(受付番号: ${data.id})`, "ok");
form.reset();
} catch (err) {
// 利用者には分かる言葉で、開発者にはログで詳細を
console.error(err);
show("送信できませんでした。時間をおいて、もう一度お試しください。", "ng");
} finally {
btn.disabled = false; // 成功でも失敗でも必ず戻す
}
});
function validate(v) {
if (!v.name) return "お名前を入力してください。";
if (v.name.length > 40) return "お名前は40文字以内で入力してください。";
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v.email))
return "メールアドレスの形式をご確認ください。";
if (!Number.isInteger(v.qty) || v.qty < 1 || v.qty > 99)
return "数量は1〜99の整数で入力してください。";
if (!v.message) return "お問い合わせ内容を入力してください。";
return null;
}
function show(text, kind) {
msg.textContent = text;
msg.className = kind ? `is-${kind}` : "";
}
画面側の検証だけでは守れません ブラウザの検証は、開発者ツールで簡単に迂回できます。受け取るサーバー側でも、必ず同じ検証をしてください 。画面側の検証は「利用者に親切に知らせるため」、サーバー側の検証は「システムを守るため」と、目的がまったく違います。
ファイルを送るときは形式が変わる
画像やPDFを送る場合、JSONではなく multipart/form-data という形式を使います。このとき Content-Type を自分で書いてはいけません 。ブラウザが区切り文字(boundary)を含めて自動で付けるためです。
ファイル付きの送信 コピー
const fd = new FormData();
fd.append("title", "請求書");
fd.append("file", document.getElementById("file").files[0]);
const res = await fetch("/api/uploads", {
method: "POST",
body: fd // ★ headers は書かない(ブラウザが自動で正しく付ける)
});
ここで実際に試せます このページの下のほう「
手を動かす道具 」に、
入力した内容から実際のcurlコマンドとfetchコードを組み立てるフォーム を置きました。上で学んだ検証・組み立ての流れが、そのまま動く形で確認できます。
一問一答
フォームのsubmitで e.preventDefault() を書かないと、何が起きますか。
ブラウザ側で検証しているのに、サーバー側でも検証が必要なのはなぜですか。
ボタンの無効化を finally で戻すのはなぜですか。
解答を見る
フォームの既定動作でページが再読み込みされ、JavaScriptの処理が途中で消える(何も起きていないように見える)。
画面の検証は開発者ツールなどで簡単に迂回できるため。サーバー側の検証がないと、不正な値がそのまま登録されたり、攻撃の入口になったりする。
成功・失敗・例外のどの経路を通っても必ず実行されるため。tryの中だけで戻すと、エラー時にボタンが押せないままになる。
このステップは理解できた
STEP 11
実装
Pythonのrequests ── 業務バッチとして動かす
目安 5日
このステップの到達点 ── Pythonで取得・作成・認証・タイムアウトを書け、毎朝自動で動く取り込み処理 の骨格を組める。鍵を環境変数から読む書き方が身につく。サーバー側の実装は、ほぼこの形です。
準備と、いちばん短い形
導入 コピー
# 作業用のフォルダを作り、その中だけで使う環境を用意する(推奨)
python3 -m venv .venv
source .venv/bin/activate # Windowsは .venv\Scripts\activate
pip install requests
get_users.py:取得して表示する コピー
import os
import requests
BASE = "https://jsonplaceholder.typicode.com"
def get_user(user_id: int) -> dict:
url = f"{BASE}/users/{user_id}"
# timeout は (接続, 読み取り) の秒数。★必ず付ける
res = requests.get(url, timeout=(3.0, 10.0))
res.raise_for_status() # 4xx・5xx なら例外を投げてくれる
return res.json()
if __name__ == "__main__":
try:
user = get_user(1)
print(user["name"], user["address"]["city"])
except requests.exceptions.Timeout:
print("時間内に応答がありませんでした")
except requests.exceptions.HTTPError as e:
print(f"APIエラー: {e.response.status_code} {e.response.text[:200]}")
except requests.exceptions.RequestException as e:
print(f"通信エラー: {e}")
raise_for_status() は、fetchと違って4xx・5xxを例外にしてくれる 便利な一行です。ただしこれを書き忘れると、fetchと同じ落とし穴に落ちます。例外は「細かいものから順に」捕まえる のが決まりで、RequestException は最後に置きます。
認証つきで、まとめて叩く(Session)
同じ相手に何度も叩くときは Session を使います。接続を使い回すので速く なり、共通ヘッダーを1か所に書けます。
client.py:実務で使う骨格 コピー
import os
import requests
class ApiClient:
def __init__(self):
# ★鍵はコードに書かず、環境変数から読む(STEP 15)
token = os.environ.get("EXAMPLE_API_TOKEN")
if not token:
raise RuntimeError("環境変数 EXAMPLE_API_TOKEN が設定されていません")
self.base = os.environ.get("EXAMPLE_API_BASE", "https://api.example.com/v1")
self.s = requests.Session()
self.s.headers.update({
"Authorization": f"Bearer {token}",
"Accept": "application/json",
# 誰からの通信か分かるようにしておく(相手のサポートに好まれる)
"User-Agent": "kodomo-batch/1.0 (system@example.co.jp)",
})
def get(self, path: str, **params) -> dict:
res = self.s.get(f"{self.base}{path}", params=params, timeout=(3.0, 30.0))
res.raise_for_status()
return res.json()
def post(self, path: str, payload: dict) -> dict:
# json= を使うと Content-Type: application/json が自動で付く
res = self.s.post(f"{self.base}{path}", json=payload, timeout=(3.0, 30.0))
res.raise_for_status()
return res.json()
if __name__ == "__main__":
api = ApiClient()
orders = api.get("/orders", status="paid", limit=20) # → ?status=paid&limit=20
print(f"{len(orders)}件 取得しました")
params= と json= に任せる クエリ文字列を自分で "?status=" + s のようにつなぐと、日本語や記号のときに壊れます。params= に辞書を渡せば、URLエンコードを自動でやってくれます 。同じく json= を使えば、json.dumps と Content-Type の両方が自動になります。手で組み立てないことが、事故を減らす最短路 です。
取り込んで、保存するところまで
daily_import.py:毎朝動かす形 コピー
import csv
import logging
from datetime import datetime, timezone, timedelta
JST = timezone(timedelta(hours=9)) # ★時差の取り違えは定番の不具合
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(levelname)s] %(message)s",
handlers=[logging.FileHandler("import.log", encoding="utf-8"),
logging.StreamHandler()],
)
log = logging.getLogger(__name__)
def main():
api = ApiClient()
today = datetime.now(JST).strftime("%Y-%m-%d")
log.info("取り込み開始 date=%s", today)
try:
orders = api.get("/orders", date=today, limit=100)
except Exception:
log.exception("取得に失敗しました") # 例外の詳細もログに残す
raise # 異常終了させ、監視で気づけるようにする
# 相手の項目名を、自社の項目名に置き換えて保存する
with open(f"orders_{today}.csv", "w", newline="", encoding="utf-8-sig") as f:
w = csv.writer(f)
w.writerow(["注文番号", "金額", "状態", "備考"])
for o in orders:
w.writerow([
o.get("id"),
o.get("amount", 0),
o.get("status", "unknown"),
o.get("note") or "", # null のとき空文字にする
])
log.info("取り込み完了 件数=%d", len(orders))
if __name__ == "__main__":
main()
ここに実務の勘所が3つ入っています
① o.get("amount", 0) … 項目が無くても落ちない書き方(o["amount"] は落ちる)
② o.get("note") or "" … null のときに空文字へ置き換える
③ encoding="utf-8-sig" … Excelで開いたときに日本語が化けない書き出し方
③は地味ですが、「渡したCSVがExcelで文字化けする」という指摘は実務で非常に多い ため、覚えておく価値があります。
定期実行のしかた できあがったスクリプトは、Linuxなら cron、Windowsなら「タスクスケジューラ」、クラウドなら各社のスケジューラで定期実行します。そのとき①必ずログをファイルに残す、②失敗したら気づける仕掛け(通知やメール)を入れる、③同じ時刻に二重起動しないようにする の3点を必ず設計してください。動かしっぱなしで誰も見ていないバッチは、いつか必ず事故になります。
一問一答
requests.get(url) に timeout を付けないと、どんな危険がありますか。
o["amount"] ではなく o.get("amount", 0) と書くのはなぜですか。
APIの鍵をソースコードに直接書いてはいけない理由を述べてください。
解答を見る
相手が応答を返さない場合、処理が永久に待ち続ける。バッチが終わらず、後続処理や翌日の実行まで止まる事故になる。
その項目が返ってこない場合、o["amount"]は例外で処理全体が止まるため。getなら既定値を使って処理を続けられる。
コードは共有・保存・バージョン管理されるため、鍵が社内外に広く残ってしまう。特にGitへ入れると履歴から消すのが困難で、公開リポジトリなら数分で悪用される。環境変数や秘密管理サービスから読む。
このステップは理解できた
STEP 12
実装
エラー処理とリトライ ── 「たまに失敗する」を前提に作る
目安 4日
このステップの到達点 ── 再試行してよい失敗と、してはいけない失敗 を区別できる。指数バックオフを実装でき、二重登録を防ぐ冪等キーの使い方を説明できる。ここから先が「実務で使えるコード」 です。
通信は、必ず失敗する
ネットワークを越える処理は、ときどき失敗するのが正常 です。相手のサーバーの再起動、一瞬の混雑、経路の切り替え。年に数回ではなく、1万回叩けば数回は失敗します 。だから実務のコードは、失敗したときにどうするかを最初から書きます。
指数バックオフ ── 待ち時間を倍々にする
失敗してすぐ再送すると、相手がまだ回復しておらず、また失敗します。全員が同時に再送すると、相手を完全に停止させる ことさえあります。そこで、1秒 → 2秒 → 4秒 → 8秒 と待ち時間を倍にしていきます。これを指数バックオフ と呼びます。
さらに、そこにランダムなばらつき(ジッター)を足す のが実務の定石です。全員がきっちり同じ秒数で再送すると、また同時に殺到してしまうためです。
retry.py:実務でそのまま使える再試行 コピー
import random
import time
import requests
RETRYABLE = {429, 500, 502, 503, 504} # 再試行してよいコード
def request_with_retry(session, method, url, max_attempts=4, **kwargs):
kwargs.setdefault("timeout", (3.0, 30.0))
for attempt in range(1, max_attempts + 1):
try:
res = session.request(method, url, **kwargs)
if res.status_code not in RETRYABLE:
res.raise_for_status() # 成功、または再試行しないエラー
return res
# 相手が「何秒待て」と言っているなら、それに従う(最優先)
wait = float(res.headers.get("Retry-After", 0)) or _backoff(attempt)
except (requests.exceptions.Timeout,
requests.exceptions.ConnectionError):
wait = _backoff(attempt) # 届かなかった場合も再試行の対象
if attempt == max_attempts:
raise RuntimeError(f"{max_attempts}回試しましたが失敗しました: {url}")
print(f"{attempt}回目が失敗。{wait:.1f}秒待って再試行します")
time.sleep(wait)
def _backoff(attempt: int) -> float:
base = 2 ** (attempt - 1) # 1, 2, 4, 8 秒
return base + random.uniform(0, 0.5) # ばらつきを足して、殺到を防ぐ
再試行の回数と合計時間に、必ず上限を 「成功するまで無限に再試行」は事故のもとです。相手が長時間停止していると、永久に終わらないバッチ ができあがります。最大4〜5回、合計1分程度 を目安に上限を決め、超えたら諦めて人に知らせる。これが正しい設計です。
冪等キー ── 二重登録を根本から防ぐ
POSTの再試行が怖いのは、「相手は成功していたのに、返事だけが届かなかった」場合 があるからです。このとき再送すると、注文が2件できます。
返事だけが失われるパターン
あなた
相手
▼ 起こりうること
POST /orders
相手は注文を登録した(成功している)
✕ 返事が届かない(切断)
成功したか分からない → もう一度送る
★ 2件目の注文ができてしまう
▼ 解決
POST /orders + Idempotency-Key: 9f2c…
「処理済み」と判断し、1件目の結果を返す(登録は1件のまま)
冪等キーの付け方 コピー
import uuid
# ★鍵は「1つの注文につき1つ」。再試行のたびに作り直してはいけない
key = str(uuid.uuid4())
res = request_with_retry(
session, "POST", f"{BASE}/orders",
headers={"Idempotency-Key": key},
json={"product_id": "A-100", "quantity": 2},
)
鍵を作る場所が重要です 。再試行のループの中で作ると、毎回違う鍵になり、意味がなくなります。「1つの業務上の依頼=1つの鍵」 と考えてください。相手のAPIが冪等キーに対応していない場合は、送信前に「同じ内容の登録が既にないか」を確認する処理を自分で入れます。
エラーを「人に伝わる形」で残す
エラーログに Error とだけ書いても、翌朝の担当者は何もできません。次の5点は必ず残してください 。
いつ (時刻。タイムゾーンつき)
どこに (メソッドとURL。ただし鍵やトークンは伏せる )
何が返ったか (ステータスコードと、本文の先頭200文字程度)
X-Request-Id (相手に問い合わせるときの決め手)
どの業務データか (注文番号など、こちらの識別子)
残すべきログの形 コピー
log.error(
"API失敗 method=%s url=%s status=%s request_id=%s order_id=%s body=%s",
"POST", url, res.status_code,
res.headers.get("X-Request-Id", "-"),
order_id,
res.text[:200], # 全部は出さない(個人情報と容量に配慮)
)
ログに書いてはいけないもの トークン、APIキー、パスワード、クレジットカード番号、個人情報の全体。ログは、開発者以外の目にも触れ、長期間残ります 。「調査のために全部出しておこう」が、そのまま情報漏えいになります。出すなら先頭4文字だけ など、伏せた形にしてください。
一問一答
再試行してよいエラーと、してはいけないエラーを、それぞれ2つ挙げてください。
待ち時間にランダムなばらつきを足すのはなぜですか。
冪等キーを、再試行のたびに新しく作ってはいけないのはなぜですか。
解答を見る
してよい:429、500系(502・503・504)、タイムアウト、接続断。してはいけない:400、422、404(何度送っても同じ結果)。
全員が同じ秒数で再送すると、回復しかけた相手に同時に殺到して再び落としてしまうため。ばらつきで送信タイミングを散らす。
鍵が毎回変わると、相手は「別の依頼」として扱い、二重登録が起きるため。1つの業務上の依頼につき1つの鍵を使い続ける。
このステップは理解できた
STEP 13
運用
大量データの扱い ── ページネーションとレート制限
目安 4日
このステップの到達点 ── 1万件のデータを、相手に迷惑をかけずに全件取得できる。3種類のページ送り方式を見分けられ、差分取得 という考え方で処理を軽くできる。
APIは、一度に全部を返さない
10万件の注文がある相手に GET /orders と頼んでも、10万件は返ってきません。ふつうは20件、100件といった単位で区切られます 。これをページネーション (ページ送り)と呼びます。全件ほしいときは、繰り返し取りに行く 必要があります。
近年のAPIはカーソル方式 が主流です。返り値に next_cursor、next、has_more といった項目があれば、それがカーソル方式の目印です。
全件を取りきる(カーソル方式) コピー
def fetch_all(api, path, **params):
items, cursor, page = [], None, 0
MAX_PAGES = 1000 # ★暴走を止める安全装置
while True:
page += 1
if page > MAX_PAGES:
raise RuntimeError("ページ数が上限を超えました。条件を見直してください")
q = dict(params, limit=100)
if cursor:
q["cursor"] = cursor
data = api.get(path, **q)
items.extend(data["items"])
print(f"{page}ページ目 累計 {len(items)}件")
cursor = data.get("next_cursor")
if not cursor: # 次が無ければ終わり
break
time.sleep(0.2) # ★相手への思いやり(叩きすぎ防止)
return items
無限ループの安全装置を必ず入れる 相手のAPIが仕様どおりでなく、いつまでも同じカーソルを返してくることがあります。最大ページ数と最大件数の上限 を必ず入れてください。これが無いと、深夜に何百万回もリクエストを投げ続け、相手から接続を遮断されます (実際に起こる事故です)。
レート制限 ── 1分あたり何回まで叩けるか
レート制限 は、APIを提供する側が「1分間に60回まで」のように定めた上限です。超えると429 が返ります。ドキュメントの「Rate Limits」に必ず書かれています。
上限に近づいたら、自分から減速する コピー
res = session.get(url, timeout=(3, 30))
remaining = int(res.headers.get("X-RateLimit-Remaining", 999))
if remaining < 5:
reset = float(res.headers.get("X-RateLimit-Reset", 60))
print(f"残り{remaining}回。{reset:.0f}秒待って再開します")
time.sleep(reset)
429を受けてから対処するのではなく、その前に自分で減速する のが上級者の書き方です。429が多発すると、相手側で「行儀の悪い利用者」として記録され、最悪の場合は利用停止になります。
差分取得 ── そもそも取る量を減らす
毎晩10万件を全部取り直すのは、相手にとっても自分にとっても無駄 です。多くのAPIは「いつ以降に更新されたか」で絞り込めます。
前回実行時刻以降だけを取る コピー
# 前回取った時刻をファイルやDBに保存しておき、次回はそれ以降だけを取る
last = load_last_run() # 例: "2026-08-13T02:00:00+09:00"
items = fetch_all(api, "/orders", updated_since=last)
# ★保存するのは「取得を始めた時刻」。終わった時刻にすると、
# 処理中に更新されたデータを取りこぼす
save_last_run(started_at)
取りこぼしを防ぐ小技 保存する時刻を数分ぶん巻き戻して おく(例:開始時刻の5分前)と、時計のずれや処理の重なりによる取りこぼしを防げます。少し重複して取ることになりますが、取りこぼしより重複のほうが、はるかに直しやすい ためです。重複は、こちら側で「同じIDなら上書き」(アップサート)にしておけば無害になります。
大量に送るとき
一括APIがあるか探す :1件ずつ1000回より、100件まとめて10回のほうが、双方にとって圧倒的に軽い
同時実行は控えめに :並行数は3〜5程度から始める。速さを求めて20並行などにすると、相手を落としかねない
途中から再開できるように :どこまで処理したかを記録しておく。1000件目で落ちたとき、最初からやり直すのは大きな損失
夜間・休日に寄せる :相手の業務時間帯を避けるのは、連携の基本的な礼儀
一問一答
ページ番号方式が、取得中にデータが増えると問題になるのはなぜですか。
429が返ったとき、何秒待てばよいかはどこで分かりますか。
差分取得で保存する時刻は、処理の「開始時刻」と「終了時刻」のどちらにすべきですか。
解答を見る
新しいデータが先頭に入ると全体が後ろにずれ、同じ件を二重に取得したり、取りこぼしたりするため。カーソル方式ならずれない。
Retry-Afterヘッダー。無い場合はX-RateLimit-Reset、それも無ければ指数バックオフで待つ。
開始時刻。終了時刻にすると、処理の実行中に更新されたデータを次回取りこぼす。さらに数分巻き戻すとより安全。
このステップは理解できた
STEP 14
運用
Webhookと運用 ── 受け取る側になる/壊れたと気づける
目安 4日
このステップの到達点 ── Webhookの向きの違いを説明でき、受け口を作るときの3つの必須処理 (すぐ200を返す・署名を検証する・重複を許容する)を言える。連携が壊れたときに、人より先に気づける仕掛けを作れる。
Webhookは「呼ばれる側」になること
ここまでは、あなたが相手のAPIを呼ぶ 側でした。Webhook (ウェブフック)は逆で、相手が、出来事が起きたときにあなたのURLを呼びに来ます 。決済完了、チャットへの投稿、配送状況の変化などの通知に使われます。
ポーリングとWebhookの違い
ポーリング(自分から何度も聞く)
あなた
相手
終わった?
まだ
終わった?
まだ
終わった?
終わった!
無駄が多く、気づくのが遅い
Webhook(相手から届く)
あなた
相手
出来事が起きた瞬間に
POST /hooks/payment
{"event":"payment.succeeded"}
速い・無駄がない(受け口を公開する責任は生じる)
受け口を作るときの必須3点
1. すぐ200を返す 重い処理を先にやると、相手がタイムアウトと判断して何度も再送 してくる。受け取ったら即200、処理は後ろで (キューに積む)
2. 署名を検証する URLは公開されている。誰でも偽の通知を送れる 。署名がなければ「決済成功」の偽通知で商品を出荷してしまう
3. 重複を許容する 同じ通知が2回以上届く前提 で作る(相手は「確実に届ける」ため再送する)。イベントIDで処理済みを記録する
webhook.py:受け口の骨格(Flask) コピー
import hashlib
import hmac
import os
from flask import Flask, request, abort
app = Flask(__name__)
SECRET = os.environ["WEBHOOK_SECRET"].encode()
@app.post("/hooks/payment")
def payment_hook():
raw = request.get_data() # ★署名検証は「生のまま」で行う
sig = request.headers.get("X-Signature", "")
# ① 署名を検証する(本物の相手からか確かめる)
expected = hmac.new(SECRET, raw, hashlib.sha256).hexdigest()
if not hmac.compare_digest(expected, sig): # ★==ではなくこれを使う
abort(401)
event = request.get_json()
event_id = event.get("id")
# ② 重複を弾く(同じ通知が何度も来る前提)
if already_processed(event_id):
return "", 200 # 二重処理せず、成功として返す
mark_processed(event_id)
# ③ 重い処理はここでやらない。キューに積んで、すぐ返す
enqueue(event)
return "", 200
hmac.compare_digest を使う理由 単純な == による比較は、一致した文字数によって処理時間がわずかに変わります 。攻撃者はその差を測って、署名を1文字ずつ突き止められます(タイミング攻撃)。compare_digest は常に同じ時間で比較するため、この攻撃を防げます。署名やトークンの比較には、必ず専用の関数を使ってください 。
開発中のWebhookの受け取り方 自分のパソコンには、外から届くURLがありません。ngrok のような道具を使うと、一時的に公開URLを作って手元に転送できます。ただしそのURLは世界中からアクセスできます 。検証用のダミーデータだけを扱い、使い終わったら必ず停止してください。
壊れたことに、人より先に気づく
連携は「静かに壊れる」 のが厄介です。エラーが出るなら気づけますが、0件を正常として処理し続ける のがいちばん怖い壊れ方です。「先月から売上データが入っていませんでした」は、実際に起きる事故です。
テスト ── 相手を呼ばずに試す
テストのたびに本物のAPIを叩くと、相手に迷惑がかかり、レート制限にも当たり、本番データを壊す危険 があります。そこでモック (偽物の応答)を使います。
test_client.py:偽の応答で試す コピー
import responses # pip install responses
@responses.activate
def test_get_order_success():
responses.add(
responses.GET, "https://api.example.com/v1/orders/1",
json={"id": 1, "status": "paid", "amount": 1200}, status=200,
)
order = ApiClient().get("/orders/1")
assert order["status"] == "paid"
@responses.activate
def test_get_order_server_error():
# ★異常系こそテストする。500のとき正しく諦められるか
responses.add(responses.GET, "https://api.example.com/v1/orders/1", status=500)
with pytest.raises(Exception):
ApiClient().get("/orders/1")
テストすべきは、成功したときよりも失敗したとき です。500が返ったとき、項目が欠けていたとき、応答が遅いとき。異常系を一度も試していないコードは、本番で必ず期待と違う動きをします 。
一問一答
Webhookの受け口で、重い処理を先にやってはいけないのはなぜですか。
署名の検証をしないと、どんな被害が起こりえますか。
連携の監視で、「異常終了の有無」だけでは不十分なのはなぜですか。
解答を見る
返事が遅れると相手がタイムアウトと判断し、同じ通知を何度も再送してくるため。受け取ったらすぐ200を返し、処理は非同期で行う。
受け口のURLは公開されているため、第三者が偽の通知(例:「決済成功」)を送れてしまう。商品の不正な出荷やデータ改ざんにつながる。
「0件を正常として処理し続ける」など、エラーを出さずに静かに壊れる場合があるため。処理件数や実行時刻も監視する必要がある。
このステップは理解できた
STEP 15
セキュリティ
安全に連携する ── 鍵の管理と、守るべき原則
目安 4日
このステップの到達点 ── APIキーを置いてよい場所と、置いてはいけない場所 を即答できる。最小権限の考え方を実践でき、「その実装で漏れませんか」という問いに理由つきで答えられる。ここは、間違えると会社に実害が出る唯一の章 です。
鍵の置き場所 ── これだけは覚える
環境変数の使い方 コピー
# Mac / Linux
export EXAMPLE_API_TOKEN="abc123..."
# Windows PowerShell
$env:EXAMPLE_API_TOKEN = "abc123..."
# .env ファイルを使う場合、必ず .gitignore に追記する
echo ".env" >> .gitignore
# 【確認】うっかり鍵をコミットしていないか、履歴を検索する
git log -p | grep -i -E "api[_-]?key|secret|token|password" | head
もし鍵を漏らしてしまったら 慌ててコミットを消しても手遅れです 。GitHubに一度でも上がった鍵は、自動収集されていると考えてください。やるべきことは1つ、ただちにその鍵を無効化し、新しい鍵を発行する ことです。そのうえで上長と情報システム部門に報告してください。隠すのが最悪の対応 です。悪用されたときの被害額は、報告の遅れに比例します。
最小権限 ── 「読むだけ」で済むなら、読むだけの鍵を
APIキーやOAuthのスコープには、たいてい細かい権限設定があります。面倒だからといって、全権限を付けた鍵を1つ作って使い回す のは、実務で最も多い設計ミスです。
データを取り込むだけの処理 → 読み取り専用 の鍵
用途ごとに別々の鍵 を発行する(漏れたとき、影響範囲をその用途に閉じ込められる)
検証環境と本番環境で、必ず別の鍵 を使う
相手がIP制限に対応しているなら、自社のIPアドレスからのみ許可 する
使わなくなった鍵はすぐ無効化 。退職者が作った鍵の棚卸しも定期的に
通信と実装で守ること
APIの返り値をそのまま innerHTML に入れない 相手のデータに <script> が含まれていた場合、それがあなたのページで実行されます(XSS)。textContent を使う か、適切にエスケープしてください。「相手は信頼できる会社だから大丈夫」ではありません。その会社に登録した第三者の入力が、そのまま流れてくる ことがあります。
個人情報を扱うときの追加の注意
API連携では、氏名・住所・メールアドレス・購買履歴といった個人情報が動きます。技術の問題である前に、会社の責任の問題 です。
取得前に、社内の規程と契約を確認する 。相手のAPIの利用規約に、データの保存や二次利用の条件が書かれています
取る項目を最小限にする 。「使わないが取れるから取る」は、漏えい時の被害を無用に大きくします
保存期間を決める 。不要になったデータは消す。持ち続けることがリスクです
国外への移転に注意 。海外のサービスに個人情報を送ることは、法令上の検討が必要になる場合があります
テストに本番データを使わない 。検証環境に本番の個人情報を入れるのは、事故の典型的な原因です
「試しに叩いてみる」の前に 業務で使うAPIを初めて試すときは、必ず検証環境(sandbox)を使ってください 。本番環境で試したPOSTが、実際の注文や請求として処理されてしまう事故は珍しくありません。検証環境が無いAPIの場合は、上長の承認を得て、影響のないデータで、範囲を決めて実施してください。
一問一答
APIキーを置いてよい場所を2つ、置いてはいけない場所を2つ挙げてください。
鍵をGitにコミットしてしまったと気づいたとき、最初にすべきことは何ですか。
APIから受け取った文字列を画面に出すとき、注意すべきことは何ですか。
解答を見る
よい:環境変数、秘密管理サービス(.env+.gitignoreも可)。不可:ソースコードへの直書き、ブラウザのJavaScript(およびチャットでの共有、URLのクエリ)。
ただちにその鍵を無効化し、新しい鍵を再発行する。そのうえで上長と情報システム部門に報告する。履歴の削除だけでは対策にならない。
そのままinnerHTMLに入れないこと。textContentを使うかエスケープする。相手経由で第三者の入力(スクリプト)が流れてくる可能性がある。
このステップは理解できた
STEP 16
実務
連携案件の進め方 ── 依頼を受けてから引き継ぐまで
目安 5日+実践
このステップの到達点 ── 「A社のデータをうちのシステムに取り込みたい」というふわっとした依頼を、実装できる形に分解できる 。設計書に何を書くべきかを言え、自分が抜けても回る形に残せる。ここまで来れば、連携案件を任せてもらえます。
依頼を受けたら、最初に確認する10項目
いきなりコードを書き始めてはいけません。この10項目を埋めるだけで、案件の8割は終わったも同然 です。埋まらない項目があれば、それが確認すべきことです。
5番の「項目の対応表」が最重要です 表計算ソフトで、相手の項目名/型/必須か/例 と、自社の項目名/型/変換ルール を並べた表を作ってください。「相手の status の "S" は、自社の「発送済」に対応する」といった値の変換規則 まで書きます。この表が、そのまま設計書であり、テスト仕様書であり、引き継ぎ資料になります。
設計書に書くこと(1〜2ページで十分)
全体図 :どのシステムからどのシステムへ、何が、いつ流れるか(文字の図で構いません)
エンドポイント :使うAPIの一覧と、それぞれの用途
項目の対応表 :上記5番の表
実行契機 :毎日6時/注文確定時/手動、など
異常時の動作 :再試行の回数、諦める条件、通知先
鍵の管理 :どこに保管し、誰が更新でき、いつ期限が切れるか
連絡先 :相手側の窓口、自社の責任者
相手に問い合わせるときの型
連携の仕事では、相手のサポートに質問する場面が必ず来ます。次の形で送ると、返答の速さと精度が大きく変わります 。
問い合わせのテンプレート コピー
■ 事象
POST /v1/orders で 422 が返り、注文を登録できません。
■ 発生日時
2026-08-14 10:32:15 JST(複数回発生、直近5回すべて)
■ 環境
検証環境(sandbox)/ クライアント: Python requests 2.32
■ 送信したリクエスト(機密情報は伏せています)
POST https://sandbox.example.com/v1/orders
Headers: Authorization: Bearer eyJh****(末尾4桁 : a1b2)
Body: {"product_id":"A-100","quantity":2,"delivery_date":"2026-08-20"}
■ 返ってきたレスポンス
HTTP 422
X-Request-Id: 7f3a9c22-1b44-4e0d-9f2a-55e1c0d3a8b1
{"error":{"code":"INVALID_FIELD","field":"delivery_date"}}
■ 確認したいこと
delivery_date の受け付け可能な書式と、指定できる日付の範囲をご教示ください。
ドキュメント(v1.4、8ページ)には「YYYY-MM-DD」と記載されています。
X-Request-Id を添えているかどうか で、相手が調べられる範囲がまるで違います。「エラーになります、直してください」だけの問い合わせは、往復が5回増えます。
引き継げる形に残す
連携は、作った人が異動しても動き続けます 。だからこそ、次の担当者が読めば分かる状態にしておくのが、実務者の最後の仕事です。
READMEを置く :何をする処理か、動かし方、環境変数の一覧、止まったときの確認手順
鍵の期限を、カレンダーに登録する :証明書やトークンの失効は、忘れたころに全体を止めます
再実行の手順を書く :「昨日ぶんをもう一度取り込みたい」は必ず起きます
相手の変更履歴を追える状態にする :APIのお知らせメールを、個人ではなく共有のメールアドレスで受け取る
これから伸ばすなら 次の一歩としては、①自分でAPIを作る側になる (FastAPIやExpressで、STEP 5の作法どおりに作ってみる)、②OpenAPIで仕様を書いてから実装する 、③iPaaS(Zapier、Power Automate、n8nなど)を触る の3つがおすすめです。特に③は、「コードを書かずに済ませられる案件」を見分ける力 がつきます。実務では、書かない判断ができる人のほうが重宝されます。
一問一答
連携の依頼を受けたとき、実装より先に作るべき資料は何ですか。
相手のサポートに問い合わせるとき、必ず添えるべき情報を3つ挙げてください。
「作って終わり」にしないために、引き継ぎで残すべきものを2つ挙げてください。
解答を見る
項目の対応表(相手の項目と自社の項目、型、必須、値の変換規則)。これが設計書・テスト仕様書・引き継ぎ資料を兼ねる。
①送ったリクエスト(URL・ヘッダー・本文。機密は伏せる)、②返ってきたステータスコードと本文、③X-Request-Idと発生日時。
README(動かし方・環境変数・障害時の確認手順)と、鍵や証明書の期限管理(カレンダー登録)。ほかに再実行手順、連絡先も。
このステップは理解できた
条件に合うステップがありません。キーワードを変えるか、「すべて」を選んでください。