FlowingDev

cURL to Code: ウェブの共通語を翻訳する

ウェブのリクエストにおける万国共通語であるcURLコマンドが、JavaScriptのfetchやPython、Go、PHPといったネイティブコードにどのように翻訳できるのかを学びましょう。

ツールを試す: cURLをコードに

一言で言うと

「cURL to Code」コンバーターは、普遍的なcURLコマンドライン構文で書かれたウェブリクエストを、JavaScript、Python、Goなどの言語ですぐに使える同等のコードに翻訳します。

どんな問題を解決するの?

はじめに、ターミナルがあった。そしてターミナルでインターネットと話したければ、ツールが必要だった。1997年、スウェーデンの開発者ダニエル・ステンバーグは、IRCボットのために為替レートを取得するツールを作った。彼はそれを「Client for URL」の略で curl と名付けた。以来、それはネットワーク操作における誰もが認めるスイスアーミーナイフへと成長した—コマンドラインから直接HTTP、FTP、SMTP、その他十数種類のプロトコルを話せる、小さくてとんでもなく強力なプログラムだ。

curl は、普遍的で、テキストベースで、とてつもなく高機能なため、APIコールを文書化するための デファクトスタンダード となった。Stripe、GitHub、TwilioといったモダンなAPIをどれでもいいから選んでみてほしい。その「はじめに」ガイドには、ほぼ間違いなく curl コマンドが載っているはずだ。これは「私たちのサーバーに送るべき正確なリクエストはこれです」と伝えるための、完璧で曖昧さのない方法なのだ。

これは素晴らしい…実際に コードを書く までは。

あなたはReactアプリの中にいる。ドキュメントにはこう書いてある: curl -X POST https://api.pizza.dev/orders -H 'Authorization: Bearer ...' --data '{"size":"large","toppings":["pepperoni","cheese"]}'

さて、これを手作業でJavaScriptの fetch コールに翻訳しなければならない。ええと… -X POST に相当する fetch の書き方はなんだっけ?よし、 method: 'POST' だな。ヘッダーの -H は?それは headers オブジェクトか。で、 --data は? body のこと?文字列をそのまま貼り付ければいいのかな?それとも JSON.stringify() が必要?待てよ、 Content-Type ヘッダーは application/json にすべきじゃないか? curl コマンドにはなかったぞ!(ネタバレ:curl はフラグによって、これを自動で追加してくれることもあれば、してくれないこともある。面白いだろ?)

この手作業での翻訳は、細かくてイライラするエラーの地雷原だ。退屈で、バグが発生しやすく、全くの脳の無駄遣い。cURL to Codeコンバーターは、完璧で忍耐強い翻訳者として機能することで、この問題を解決する。APIドキュメントの共通言語を取り込み、あなたのアプリケーションが話す特定の方言に変換してくれるので、時間とバグ、そして「なんで400 Bad Requestなんだよ!」と虚空に向かって叫ぶ手間を省いてくれるのだ。

内部の仕組み

cURL to Codeコンバーターの核心は、特殊なパーサーだ。実際に curl コマンドを 実行 するわけではない。代わりに、コマンドをテキストの文字列として読み込み、トークンごとに分解し、各部分をターゲットとなるプログラミング言語の対応する概念にマッピングする。

少し複雑なコマンドの翻訳を分解してみよう:

curl -X POST 'https://api.example.com/v1/users' \
  -H 'Authorization: Bearer my-secret-token' \
  -H 'Content-Type: application/json' \
  --data-raw '{"name": "Alice", "role": "admin"}' \
  -L

優れたパーサーは、このコマンドをいくつかの段階で処理する。

### コマンド、引数、URL

まず、パーサーは引用符を尊重しながら、コマンドをスペースで分割する。curl、-X、POST、'https://api.example.com/v1/users' などを認識する。

  • curl: これでコマンドの種類を特定する。パーサーはcURL構文を扱っていることを知る。
  • 'https://api.example.com/v1/users': これはフラグ(つまり - で始まらない)ではない最初の引数だ。パーサーはこれをターゲットURLとして正しく識別する。これは、ほとんどのHTTPライブラリの主要な引数になる。
// JavaScript fetch
fetch('https://api.example.com/v1/users', { /* ... options */ });
# Python requests
requests.post('https://api.example.com/v1/users', **options)

### メソッド: -X POST

-X(または --request)フラグは、HTTPメソッドを明示的に設定する。パーサーは -X を見て、次のトークンである POST がメソッドであることを知る。-X がない場合は GET がデフォルトとなる(ただし、-d のようなデータフラグが使われている場合は POST を意味する)。

これはターゲット言語のメソッドパラメータに直接マッピングされる。

// JavaScript fetch
{
  method: 'POST'
}
// Go net/http
req, err := http.NewRequest("POST", url, ...)

### ヘッダー: -H

-H(または --header)フラグは複数回出現することがある。パーサーはそれらすべてを拾い集め、キーと値の構造にまとめる。

  • -H 'Authorization: Bearer my-secret-token' -> Authorization: Bearer my-secret-token
  • -H 'Content-Type: application/json' -> Content-Type: application/json

このコレクションは、生成されたコードで辞書、マップ、またはプレーンオブジェクトになる。

// JavaScript fetch
{
  headers: {
    'Authorization': 'Bearer my-secret-token',
    'Content-Type': 'application/json'
  }
}

### ボディ: --data-raw

ここが面白いところで、優れたコンバーターの真価が問われる部分だ。cURLにはデータを送信するための多くのフラグがある:

  • -d, --data: データをURLエンコードして送信する。デフォルトで Content-Type を application/x-www-form-urlencoded に設定する。
  • --data-raw: データを余計な処理をせずに、そのまま送信する。
  • --data-binary: データをバイナリ形式で送信する。
  • -F, --form: multipart/form-data リクエストを作成する。通常はファイルのアップロードに使用する。

今回の例では --data-raw を使用している。これはボディが、おそらくJSONとして、事前にフォーマットされていることを強く示唆している。パーサーは次の文字列を取得する:'{"name": "Alice", "role": "admin"}'。

そしてコンバーターは、この文字列をリクエストのボディに入れる。Pythonのような言語では、文字列を直接渡すことができる。JavaScriptの場合は、ユーザーにネイティブなJSオブジェクトを見せて、それを JSON.stringify() でラップするのがベストプラクティスだ。

// JavaScript fetch
{
  body: JSON.stringify({
    name: "Alice",
    role: "admin"
  })
}
# Python requests
# 'requests'ライブラリは賢い。文字列とJSONのContent-Typeを提供すれば...
# 文字列を送信してくれる。または、jsonヘルパーを使うこともできる:
response = requests.post(url, headers=headers, json={"name": "Alice", "role": "admin"})

### その他のフラグ: -L

-L(または --location)フラグは、curl にHTTPリダイレクト(例:301や302レスポンス)を追従するように指示する。パーサーはこれをターゲットライブラリの同等のオプションにマッピングする。

// JavaScript fetch
{
  redirect: 'follow'
}

これらすべてをまとめると、パーサーは翻訳されたこれらの断片を組み立てて、完全で文法的に正しいコードブロックを生成する。

一般的なフラグの簡単なマッピングは以下の通りだ:

cURLフラグ 意味 マッピング先...
(フラグなし) URL ターゲットURLの引数
-X, --request HTTPメソッド (GET, POSTなど) methodプロパティ、関数名
-H, --header リクエストヘッダー headersオブジェクト/辞書
-d, --data リクエストボディ (URLエンコード) bodyプロパティ、dataパラメータ
--data-raw リクエストボディ (そのまま) bodyプロパティ、dataパラメータ
-u, --user Basic認証 Authorizationヘッダー (Basic <base64>)
-L, --location リダイレクトを追従 redirect: 'follow'オプション
--compressed 圧縮されたレスポンスを要求 Accept-Encodingヘッダー
-i, --include 出力にレスポンスヘッダーを含める (無視される; 出力専用フラグ)

現場でのストーリー

### フロントエンド開発者と紛らわしいフラグ

フロントエンド開発者のクロエは、サードパーティの配送APIを統合していた。ドキュメントには配送料金を取得するための curl コマンドが記載されていた。彼女はヘッダーとJSONボディを丁寧に fetch リクエストにコピーしたが、毎回 400 Bad Request で失敗した。1時間も頭を悩ませた後、彼女は curl の例が --data-raw ではなく -d を使っていることに気づいた。彼女の fetch コールは生のJSONを送信していたが、サーバーは -d の意味合いに従ってURLエンコードされた文字列を期待していたのだ。APIの設計はイマイチだったが、curl コマンドは技術的に正しかった。イライラした彼女は、コマンドをcURLコンバーターに貼り付けた。すると、データを正しく URLSearchParams オブジェクトでラップするJavaScriptスニペットが出力された。リクエストは即座に成功した。

教訓: cURLコンバーターは、経験豊富な開発者でさえ見逃しがちな curl フラグの微妙で暗黙的な動作を理解しており、何時間ものデバッグ作業を節約してくれる。

### DevOpsエンジニアと午前3時のWebhook

DevOpsエンジニアのベンは、緊急アラートシステムをセットアップしていた。メインデータベースのCPU使用率が5分間95%を超えた場合、スクリプトがPagerDutyのWebhookにメッセージを投稿する必要があった。PagerDutyのドキュメントには、きれいな curl コマンドが提供されていた。ベンの自動化スクリプトはGoで書かれていた。彼はGoの net/http の構文を調べ、リクエストを作成し、ヘッダーを設定し、JSONボディを添付する方法を理解するのに15分を費やすこともできただろう。代わりに、彼は curl コマンドをコンバーターに放り込み、「Go」を選択し、5秒で必要な正確なコードを手に入れた。彼はそれをスクリプトに貼り付け、テストし、次の作業に移った。

教訓: スクリプティングや自動化において、cURLコンバーターは生産性を大幅に向上させる。言語固有のHTTPクライアント構文を調べるために必要なコンテキストスイッチをなくしてくれるからだ。

### 初心者と「ロゼッタストーン」

ウェブ開発を学び始めたばかりのサムは、APIについて聞いたところだった。「コードが他のコードと話す」という概念はまだ曖昧だった。彼は面白そうな無料の天気APIを見つけ、そのドキュメントにはロンドンの予報を取得するための curl コマンドが載っていた。彼はそれをターミナルで実行し、JSONデータのストリームが現れるのを見た。まるで魔法のようだった!しかし、どうすればそのデータをウェブページに表示できるのだろう?彼は curl コマンドをコンバーターに貼り付け、fetch コードを目にした。突然、すべてが繋がった。コマンドのURLは fetch の最初の引数になっていた。-H フラグは headers オブジェクトになった。抽象的なターミナルコマンドが、彼がプロジェクトで直接使える具体的で読みやすいコードブロックに変わったのだ。

教訓: cURLコンバーターは「ロゼッタストーン」として機能し、抽象的なコマンドと現実のコードとの間のギャップを埋める。そのため、学習において非常に価値のあるツールとなる。

よくある間違いと落とし穴

  • シェルコンテキストを忘れること。 curl "https://api.com?q=$USER" のようなコマンドでは、$USER 変数は curl が実行される 前 にシェルによって置き換えられる。コンバーターは文字列 "$USER" しか見ることができず、その値を知ることはできない。シェルの展開に注意し、「最終的な」コマンドをコピーするようにしよう。
  • -d 対 --data-raw の罠。 これは古典的な間違いだ。APIがJSONを期待している場合、ほぼ間違いなく Content-Type: application/json ヘッダーと共に --data-raw を使いたい。-d を使うとJSONがURLエンコードされ({ は %7B に、" は %22 になるなど)、ほとんどのJSON APIは処理に失敗するだろう。
  • ファイルアップロードを無視すること。 multipart/form-data リクエスト(-F や --form を使用)の変換はトリッキーだ。生成されたコードはファイルの読み込みを処理し、特別な FormData オブジェクトを作成する必要がある。単純なコンバーターはここで失敗することが多く、ファイルの内容の代わりにファイル名を文字列として送信するコードを生成してしまう。
  • リクエストフラグと出力フラグを混同すること。 -v(詳細表示)、-s(サイレント)、-o file.txt(ファイルに出力)のようなフラグは、curl が情報をどのように表示するかを制御するものだ。これらはサーバーに送信されるHTTPリクエストの一部ではない。優れたコンバーターはこれらを認識して無視するべきだ。HTTPクライアントライブラリには同等のものがないからだ。
  • シングルクォートとダブルクォート。 bash などのシェルでは、シングルクォート(')はその内容を文字通りに扱うが、ダブルクォート(")は変数の展開を許容する。これは、コンバーターが実際に目にする文字列に影響を与える可能性がある。コピーしているものが、送信したいものと本当に同じか常に確認しよう。

なぜ注目すべきか

ウェブに触れるすべての開発者は、遅かれ早かれ curl コマンドに出くわすことになる。それらを迅速かつ確実に翻訳する方法を知っていることは、強力な武器になる。

  • APIを利用するとき: これが主なユースケースだ。APIドキュメントは curl で書かれている。あなたのアプリはそうではない。そのギャップを埋めよう。
  • ネットワークリクエストをデバッグするとき: 最近のブラウザの開発者ツールでは、ネットワークリクエストを右クリックして「cURLとしてコピー」できる。これをコンバーターに貼り付けることで、ブラウザからの正確なリクエストをPythonやNode.jsのスクリプトで再現し、より分離された強力なデバッグが可能になる。
  • 自動化やスクリプトを書くとき: Pythonスクリプト、Goのユーティリティ、PHPのcronジョブからエンドポイントを叩く必要がある? curl コマンドを見つけて変換しよう。毎回ゼロから構文を調べるより速い。
  • 新しい言語を学ぶとき: curl は知っているが、AxiosやPythonの requests、Goの net/http は初めてという場合、コンバーターは素晴らしい学習ツールになる。慣れない環境で、見慣れたリクエストをその言語らしい方法で実行する方法を示してくれる。

さらに詳しく

  • Everything cURL: cURLの生みの親、ダニエル・ステンバーグによる決定版ガイド。
  • curl Man Page: すべてのフラグとオプションに関する公式の網羅的なリファレンス。
  • MDN: Fetch API の利用: モダンJavaScriptでウェブリクエストを行うためのバイブル。(日本語版)
  • Python requests Quickstart: おそらくあらゆる言語の中で最も愛されているHTTPクライアントライブラリのドキュメント。
  • RFC 9110: HTTP Semantics: HTTP自体の内部で何が起こっているのかを本当に、本当に 知りたいときのために。
  • Wikipedia: cURL: ツールの歴史と機能に関する高レベルな概要。(日本語版)

理論はOK。さあ手を動かそう — 100%ブラウザ内で。

ツールを試す: cURLをコードに