WEBド素人がXのPost APIをCodex+Cursorだけで実装した話

## やりたいこと
Xに投稿するWEB APIをGUIから操作できるWEBアプリの作成

認証にはOAuthが必要で、PostmanなどのAPIツールから単純にコールすることはできず
認証用のアプリを準備するのもWEB素人の私にはハードルが高すぎる。

そこで、CodexとCursorを使用してこの辺りを丸っとノーコーディングで実装できるかをトライしてみる。

## codexでモックアップを準備

CodexにPythonのモックアップの準備から始めさせる。
以下の通り、Sendを押すとResponseが返還されるモックが作成される。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4109347/96b4f7db-3a86-45a2-a9ac-beef283ff785.png)

このモックを以て実行環境の初期構築完了。（プロンプトは単純なので省略）

## Post API用のOAuth2.0アプリをXのdeveloper consoleで準備
https://developer.twitter.com/en/portal/dashboard
※事前に無料の開発者登録が必要


Projects & appsから新規でOAuth2.0用のアプリケーションの準備

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4109347/0349c6d0-30ea-4630-bead-51391153ecd4.png)

CallbackURLを登録
https://yourdomain.com/callback
ローカルホストでのテスト用のcallback URLも登録する。
※Website URLはデプロイ後の環境を指定（現段階でまだ環境が無ければ適当でもOK）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4109347/34cdac00-32e3-468c-8dba-1fe7c0779b57.png)


その後、発行されるclient IDとsecretを内部にコピーして終了

## Codex実行

```ruby:タスクプロンプト
### Task
既存のX APIモックページを、実際にツイート投稿を行う実装に置き換えてください。

### Context
- Repository scope: X APIコール画面関連のみ
- Relevant files / functions:
  - `POST https://api.twitter.com/2/tweets` エンドポイントへの実装
  - 認証はOAuth 2.0を使用する
  - OAuth 2.0のトークンを取得するためのX API用のCallback URLを実装すること
  - X API用のCallback URLはReadmeやDocの必要な個所に記録すること
  - `text`（必須）`, `media` を入力パラメータとして扱う 
  - mediaについてはGUIから画像をアップロード機能を実装すること
  - 注意：GUI側でツイート本文を入力し送信できるモックは既に存在するのでここを上書き
- Known constraints:
  - APIキー等は変数として管理（ハードコーディング禁止）
  - API仕様: https://docs.x.com/x-api/posts/creation-of-a-post

### Expected Output
- 実API呼び出しコード（POST）への差し替え
- APIキー等の変数名を明確に定義
```

画面はこの指示だけで出来上がった。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4109347/f162d261-2318-4120-a103-8ab00acbdcbe.png)

Xアカウントでログインリンクから、ログインすることでOAuth2.0で使うトークンを取得して、APIをコールする想定でしっかり記載されている。
しかし、以下のように外部アプリの連携を行うとエラーが発生する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4109347/f9ba7e4e-6889-4081-9731-e56a75e989f5.png)


エラー：
oauthlib.oauth2.rfc6749.errors.UnauthorizedClientError
oauthlib.oauth2.rfc6749.errors.UnauthorizedClientError: (unauthorized_client) Missing valid authorization header


## エラー解消にはCursorを使用してみる。
Cursorのチャットに質問すると修正案まで返してくれる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4109347/18a8ceca-99ab-4d39-8fdb-998300279c90.png)

この修正をAcceptすることでエラーが解消された。

## 実行

:::note warn
ローカルで実行するときは、https通信が使用できないため、実行時に環境変数を設定しておく（実環境では実施しない。）
set OAUTHLIB_INSECURE_TRANSPORT=1
:::

投稿成功
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4109347/fb5c31cc-69e1-4fa2-93ee-0afa4da80999.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4109347/dd5f940f-82fb-4008-9acc-fb581e57608b.png)


## おまけ：反省と修正比較

画像も一緒にアップロードできればと思ったが、画像のAPIはまたバージョンが違う
画像のアップロード機能はいったんCodexに依頼して削除する必要がある。

この修正をCodexとCursurに実施させてみた。
```ruby:タスクプロンプト
Xの画像Postは現在のAPIでは対応していないようです。画像Postに関連する機能を取り除いてください。
```

結果として、Codexの修正はこの短文では、XのAPIと関係ない部分も削除してしまった。


## CodexとCursorの使い分け：
・大枠はCodex、微修正はCursorodexで大枠のアプリ構成や機能実装を一気に生成
・Cursorで手元のリポジトリに対してピンポイントで修正・最適化

Cursorはローカルのコンテキストに強く、コミットを残さず即座に反映できる点も魅力です。Codexのようにブランチへのコミットがまいかいふえたり、diffを大量に追う必要もなく、本当に“軽微な修正”が軽微なまま扱えるのが最大の利点かもしれません。