Pinata の Files API v3 でグループ機能を使用する際のはまりポイントと解決策をまとめます。
背景
Pinata でアップロードしたファイルをグループで管理し、特定のグループに属するファイルのみを取得したいケースがあります。例えば、NFT登録フォームで使用する入力画像を「input」グループに格納し、そのグループからのみ画像を選択できるようにする場合などです。
はまりポイント
1. レガシーAPI と V3 API のファイル管理は分離されている
問題 : レガシーAPI(pinFileToIPFS)でアップロードしたファイルは、V3 API(/v3/files)では取得できません。逆も同様です。
レガシーAPI (pinList) → レガシーでアップロードしたファイルのみ表示
V3 API (/v3/files) → V3でアップロードしたファイルのみ表示
解決策 : どちらかのAPIに統一する。V3 APIに移行する場合は、新規アップロードからV3を使用し、既存ファイルは手動でグループに追加するか、マイグレーションを検討。
2. V3 API のエンドポイントには {network} パラメータが必要
問題 : V3 API のエンドポイントは {network} パラメータ(public または private)が必須。
❌ GET /v3/files?group={group_id}
✅ GET /v3/files/public?group={group_id}
解決策 : 通常のIPFSファイルには public を使用。
3. グループフィルタのパラメータ名が異なる
問題 : アップロード時とリスト取得時でパラメータ名が異なる。
| 操作 | パラメータ名 |
|---|---|
| アップロード | group_id |
| リスト取得 | group |
4. JWT の種類による認証の違い
問題 : Scoped Key で生成した JWT は V3 API で動作しない場合がある。
解決策 : Pinata ダッシュボードで Admin 権限を持つ新しい API Key を生成。
実装例
環境変数
# Pinata JWT (Admin権限推奨)
PINATA_JWT=eyJhbGciOiJIUzI1NiIs...
# グループID(Pinataダッシュボードで確認)
PINATA_INPUT_GROUP_ID=b6543372-dadd-482d-9409-98e9c2e079ff
アップロード API(V3)
// app/api/pinata/upload/route.ts
import { NextRequest, NextResponse } from "next/server";
const INPUT_GROUP_ID = process.env.PINATA_INPUT_GROUP_ID;
export async function POST(request: NextRequest) {
const formData = await request.formData();
const file = formData.get("file") as File;
const pinataFormData = new FormData();
pinataFormData.append("file", file);
pinataFormData.append("name", file.name);
pinataFormData.append("network", "public"); // 必須
pinataFormData.append("group_id", INPUT_GROUP_ID); // グループに追加
const response = await fetch(
"https://uploads.pinata.cloud/v3/files", // V3 アップロードエンドポイント
{
method: "POST",
headers: {
Authorization: `Bearer ${process.env.PINATA_JWT}`,
},
body: pinataFormData,
}
);
const data = await response.json();
return NextResponse.json({
success: true,
ipfsHash: data.data.cid, // V3では data.data.cid
});
}
リスト取得 API(V3)
// app/api/pinata/list/route.ts
import { NextRequest, NextResponse } from "next/server";
const INPUT_GROUP_ID = process.env.PINATA_INPUT_GROUP_ID;
export async function GET(request: NextRequest) {
const limit = request.nextUrl.searchParams.get("limit") || "50";
// V3 エンドポイント(/public が必須)
const url = new URL("https://api.pinata.cloud/v3/files/public");
url.searchParams.set("group", INPUT_GROUP_ID); // group_id ではなく group
url.searchParams.set("limit", limit);
const response = await fetch(url.toString(), {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.PINATA_JWT}`,
},
});
const data = await response.json();
// V3 レスポンス構造
const files = data.data?.files || [];
return NextResponse.json({
success: true,
images: files.map((file) => ({
ipfsHash: file.cid,
name: file.name,
datePinned: file.created_at,
size: file.size,
})),
});
}
V3 API エンドポイント一覧
| 操作 | メソッド | エンドポイント |
|---|---|---|
| ファイルアップロード | POST | https://uploads.pinata.cloud/v3/files |
| ファイルリスト | GET | https://api.pinata.cloud/v3/files/{network} |
| グループ作成 | POST | https://api.pinata.cloud/v3/groups/{network} |
| グループにファイル追加 | PUT | https://api.pinata.cloud/v3/groups/{network}/{group_id}/ids/{file_id} |
グループIDの確認方法
Pinata ダッシュボードでグループを選択すると、URLにグループIDが表示されます:
https://app.pinata.cloud/ipfs/groups/b6543372-dadd-482d-9409-98e9c2e079ff
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
これがグループID
または、V3 API でファイルを取得すると、各ファイルの group_id フィールドで確認できます:
{
"data": {
"files": [
{
"id": "019b5f80-c298-7d6f-9ff0-58b2e16bfc38",
"name": "example.jpg",
"cid": "bafybei...",
"group_id": "b6543372-dadd-482d-9409-98e9c2e079ff"
}
]
}
}
デバッグのコツ
- まずグループフィルタなしでリスト取得 : ファイルが存在するか確認
- レスポンスの
group_idを確認: 期待するグループに属しているか確認 - エンドポイントURLを確認 :
/publicや/privateが含まれているか確認
まとめ
| 項目 | レガシーAPI | V3 API |
|---|---|---|
| アップロード | api.pinata.cloud/pinning/pinFileToIPFS | uploads.pinata.cloud/v3/files |
| リスト | api.pinata.cloud/data/pinList | api.pinata.cloud/v3/files/{network} |
| グループパラメータ(アップロード) | なし | group_id |
| グループパラメータ(リスト) | groupId | group |
| CIDの取得 | response.IpfsHash | response.data.cid |
V3 API への移行時は、これらの違いに注意して実装してください。