mcpField
V
vscode-rest-client-generator

vscode-rest-client-generator

github.com/patricio0312revテスト・品質63246 回インストール41 週間前に更新
リポジトリ

vscode-rest-client-generator について

APIルートからVS Code REST Client用の`.http`ファイルを生成する開発者向けスキルです。リクエスト整理、環境変数、認証、サンプル本文、レスポンス連携を扱います。

主な機能

  1. 01APIルート情報の抽出
  2. 02整理された`.http`リクエストファイル
  3. 03環境別変数
  4. 04Bearer、Basic、API Key認証
  5. 05レスポンス変数の連携
  6. 06GraphQLリクエスト例

活用シーン

  1. 01VS Code内のAPIテストを準備
  2. 02環境ごとにリクエストを切り替え
  3. 03後続リクエストでログイン応答を再利用
  4. 04エディターからGraphQLエンドポイントをテスト

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/patricio0312rev/skills --skill vscode-rest-client-generator

インストール後、Claude Code で/vscode-rest-client-generatorを入力して読み込まれたか確認します。

詳細

VS Code REST Client ジェネレーター

エディターを離れることなく、VS Code でインライン API テストを行うための .http ファイルを生成します。

基本的なワークフロー

  1. ルートをスキャン:すべての API ルート定義を検索します
  2. メタデータを抽出:メソッド、パス、パラメーター、ボディを抽出します
  3. .http ファイルを作成:リソースごと、または単一ファイルに整理します
  4. 変数を追加:環境ごとの値を設定します
  5. 認証を設定:Bearer、Basic、API Key
  6. サンプルを含める:サンプルデータを使ったリクエストボディを含めます

ファイル構成のオプション

# Option 1: Single file
api-requests.http

# Option 2: By resource
http/
├── users.http
├── products.http
├── orders.http
└── auth.http

# Option 3: By environment
http/
├── local.http
├── staging.http
└── production.http

.http ファイルの基本構文

### Get all users
GET {{baseUrl}}/users
Authorization: Bearer {{authToken}}

### Get user by ID
GET {{baseUrl}}/users/{{userId}}
Authorization: Bearer {{authToken}}

### Create user
POST {{baseUrl}}/users
Content-Type: application/json
Authorization: Bearer {{authToken}}

{
  "name": "John Doe",
  "email": "[email protected]"
}

### Update user
PUT {{baseUrl}}/users/{{userId}}
Content-Type: application/json
Authorization: Bearer {{authToken}}

{
  "name": "John Updated"
}

### Delete user
DELETE {{baseUrl}}/users/{{userId}}
Authorization: Bearer {{authToken}}

環境変数

# settings.json or .vscode/settings.json
# {
#   "rest-client.environmentVariables": {
#     "$shared": {
#       "version": "v1"
#     },
#     "local": {
#       "baseUrl": "http://localhost:3000/api",
#       "authToken": "local-dev-token"
#     },
#     "staging": {
#       "baseUrl": "https://staging-api.example.com",
#       "authToken": ""
#     },
#     "production": {
#       "baseUrl": "https://api.example.com",
#       "authToken": ""
#     }
#   }
# }

### Variables Reference
# Use Ctrl+Alt+E (Cmd+Alt+E on Mac) to switch environments
# {{$shared.version}} - shared across all environments
# {{baseUrl}} - from current environment
# {{$timestamp}} - current timestamp
# {{$randomInt min max}} - random integer
# {{$guid}} - random UUID

ジェネレータースクリプト

// scripts/generate-http-files.ts
import * as fs from "fs";
import * as path from "path";

interface RouteInfo {
  method: string;
  path: string;
  name: string;
  description?: string;
  body?: object;
  headers?: Record<string, string>;
  queryParams?: { name: string; value: string; optional?: boolean }[];
}

interface HttpFileOptions {
  baseUrlVar: string;
  authType?: "bearer" | "basic" | "apikey";
  authVar?: string;
  includeComments?: boolean;
}

function generateHttpFile(
  routes: RouteInfo[],
  options: HttpFileOptions
): string {
  const lines: string[] = [];

  // Add file header
  lines.push("# Auto-generated API requests");
  lines.push(`# Base URL: {{${options.baseUrlVar}}}`);
  lines.push("# Switch environment: Ctrl+Alt+E (Cmd+Alt+E on Mac)");
  lines.push("");

  for (const route of routes) {
    // Request separator and name
    lines.push(`### ${route.name}`);

    if (route.description) {
      lines.push(`# ${route.description}`);
    }

    // Method and URL
    let url = `{{${options.baseUrlVar}}}${route.path}`;

    // Convert :param to {{param}}
    url = url.replace(/:(\w+)/g, "{{$1}}");

    // Add query params
    if (route.queryParams?.length) {
      const queryString = route.queryParams
        .map((p) => `${p.name}=${p.value}`)
        .join("&");
      url += `?${queryString}`;
    }

    lines.push(`${route.method} ${url}`);

    // Headers
    if (["POST", "PUT", "PATCH"].includes(route.method)) {
      lines.push("Content-Type: application/json");
    }

    // Authentication
    if (options.authType === "bearer" && options.authVar) {
      lines.push(`Authorization: Bearer {{${options.authVar}}}`);
    } else if (options.authType === "basic") {
      lines.push(`Authorization: Basic {{${options.authVar}}}`);
    } else if (options.authType === "apikey") {
      lines.push(`X-API-Key: {{${options.authVar}}}`);
    }

    // Custom headers
    if (route.headers) {
      for (const [key, value] of Object.entries(route.headers)) {
        lines.push(`${key}: ${value}`);
      }
    }

    // Request body
    if (route.body && ["POST", "PUT", "PATCH"].includes(route.method)) {
      lines.push("");
      lines.push(JSON.stringify(route.body, null, 2));
    }

    lines.push("");
    lines.push("");
  }

  return lines.join("\n");
}

function generateHttpFilesByResource(
  routes: RouteInfo[],
  outputDir: string,
  options: HttpFileOptions
): void {
  const groupedRoutes = groupRoutesByResource(routes);

  if (!fs.existsSync(outputDir)) {
    fs.mkdirSync(outputDir, { recursive: true });
  }

  for (const [resource, resourceRoutes] of Object.entries(groupedRoutes)) {
    const content = generateHttpFile(resourceRoutes, options);
    const filePath = path.join(outputDir, `${resource}.http`);
    fs.writeFileSync(filePath, content);
    console.log(`Generated ${filePath}`);
  }
}

function groupRoutesByResource(
  routes: RouteInfo[]
): Record<string, RouteInfo[]> {
  const groups: Record<string, RouteInfo[]> = {};

  for (const route of routes) {
    const parts = route.path.split("/").filter(Boolean);
    const resource = parts[0] || "api";

    if (!groups[resource]) {
      groups[resource] = [];
    }
    groups[resource].push(route);
  }

  return groups;
}

完全なサンプルファイル

users.http
# Users API
# Environment: {{$env}}

@baseUrl = {{baseUrl}}
@authToken = {{authToken}}

### List all users
# @name listUsers
GET {{baseUrl}}/users?page=1&limit=10
Authorization: Bearer {{authToken}}

### Get user by ID
# @name getUser
GET {{baseUrl}}/users/{{userId}}
Authorization: Bearer {{authToken}}

### Create new user
# @name createUser
POST {{baseUrl}}/users
Content-Type: application/json
Authorization: Bearer {{authToken}}

{
  "name": "John Doe",
  "email": "[email protected]",
  "role": "user"
}

### Update user
# @name updateUser
PUT {{baseUrl}}/users/{{userId}}
Content-Type: application/json
Authorization: Bearer {{authToken}}

{
  "name": "John Updated",
  "email": "[email protected]"
}

### Partial update user
# @name patchUser
PATCH {{baseUrl}}/users/{{userId}}
Content-Type: application/json
Authorization: Bearer {{authToken}}

{
  "status": "active"
}

### Delete user
# @name deleteUser
DELETE {{baseUrl}}/users/{{userId}}
Authorization: Bearer {{authToken}}

### Upload user avatar
# @name uploadAvatar
POST {{baseUrl}}/users/{{userId}}/avatar
Content-Type: multipart/form-data; boundary=----FormBoundary

------FormBoundary
Content-Disposition: form-data; name="avatar"; filename="avatar.png"
Content-Type: image/png

< ./avatar.png
------FormBoundary--
auth.http
# Authentication API

@baseUrl = {{baseUrl}}

### Login
# @name login
POST {{baseUrl}}/auth/login
Content-Type: application/json

{
  "email": "[email protected]",
  "password": "password123"
}

### Use token from login response
@authToken = {{login.response.body.$.token}}

### Register
# @name register
POST {{baseUrl}}/auth/register
Content-Type: application/json

{
  "name": "New User",
  "email": "[email protected]",
  "password": "securepassword123"
}

### Refresh token
POST {{baseUrl}}/auth/refresh
Content-Type: application/json
Authorization: Bearer {{authToken}}

{
  "refreshToken": "{{refreshToken}}"
}

### Logout
POST {{baseUrl}}/auth/logout
Authorization: Bearer {{authToken}}

### Forgot password
POST {{baseUrl}}/auth/forgot-password
Content-Type: application/json

{
  "email": "[email protected]"
}

### Reset password
POST {{baseUrl}}/auth/reset-password
Content-Type: application/json

{
  "token": "{{resetToken}}",
  "password": "newpassword123"
}

高度な機能

レスポンス変数
### Login and capture token
# @name login
POST {{baseUrl}}/auth/login
Content-Type: application/json

{
  "email": "[email protected]",
  "password": "password123"
}

### Use captured token
@token = {{login.response.body.token}}
@userId = {{login.response.body.user.id}}

### Make authenticated request
GET {{baseUrl}}/users/{{userId}}
Authorization: Bearer {{token}}
動的変数
### Create with random data
POST {{baseUrl}}/users
Content-Type: application/json

{
  "id": "{{$guid}}",
  "email": "user-{{$randomInt 1000 9999}}@example.com",
  "createdAt": "{{$timestamp}}"
}

### Available dynamic variables:
# {{$guid}} - UUID v4
# {{$randomInt min max}} - random integer
# {{$timestamp}} - Unix timestamp
# {{$timestamp offset option}} - with offset
# {{$datetime rfc1123}} - formatted date
# {{$localDatetime iso8601}} - local datetime
# {{$processEnv VAR_NAME}} - environment variable
GraphQL リクエスト
### GraphQL Query
POST {{baseUrl}}/graphql
Content-Type: application/json
Authorization: Bearer {{authToken}}
X-REQUEST-TYPE: GraphQL

{
  "query": "query GetUsers($first: Int) { users(first: $first) { id name email } }",
  "variables": {
    "first": 10
  }
}

### GraphQL Mutation
POST {{baseUrl}}/graphql
Content-Type: application/json
Authorization: Bearer {{authToken}}
X-REQUEST-TYPE: GraphQL

{
  "query": "mutation CreateUser($input: CreateUserInput!) { createUser(input: $input) { id name } }",
  "variables": {
    "input": {
      "name": "New User",
      "email": "[email protected]"
    }
  }
}

VS Code の設定

// .vscode/settings.json
{
  "rest-client.environmentVariables": {
    "$shared": {
      "version": "v1",
      "contentType": "application/json"
    },
    "local": {
      "baseUrl": "http://localhost:3000/api",
      "authToken": "",
      "userId": "1"
    },
    "staging": {
      "baseUrl": "https://staging-api.example.com",
      "authToken": "",
      "userId": "test-123"
    },
    "production": {
      "baseUrl": "https://api.example.com",
      "authToken": "",
      "userId": ""
    }
  },
  "rest-client.previewResponseInUntitledDocument": true,
  "rest-client.timeoutinmilliseconds": 10000,
  "rest-client.followRedirect": true,
  "rest-client.defaultHeaders": {
    "Accept": "application/json",
    "User-Agent": "rest-client"
  }
}

CLI スクリプト

#!/usr/bin/env node
// scripts/http-gen.ts
import * as fs from "fs";
import { program } from "commander";

program
  .name("http-gen")
  .description("Generate .http files from API routes")
  .option("-f, --framework <type>", "Framework type", "express")
  .option("-s, --source <path>", "Source directory", "./src")
  .option("-o, --output <path>", "Output directory", "./http")
  .option("--single-file", "Generate single file instead of per-resource")
  .option("-a, --auth <type>", "Auth type (bearer|basic|apikey)")
  .parse();

const options = program.opts();

async function main() {
  const routes = await scanRoutes(options.framework, options.source);

  const httpOptions = {
    baseUrlVar: "baseUrl",
    authType: options.auth,
    authVar: "authToken",
  };

  if (options.singleFile) {
    const content = generateHttpFile(routes, httpOptions);
    fs.writeFileSync(path.join(options.output, "api.http"), content);
  } else {
    generateHttpFilesByResource(routes, options.output, httpOptions);
  }

  console.log(`Generated .http files in ${options.output}`);
}

main();

ベストプラクティス

  1. 名前付きリクエストを使用:変数を取得するには # @name requestName を追加します
  2. リソースごとに整理:API リソースごとに .http ファイルを 1 つ作成します
  3. 環境を切り替え:設定で複数の環境を構成します
  4. サンプルを含める:現実的なサンプルデータをあらかじめボディに入力します
  5. コメントを追加:各リクエストの目的を記載します
  6. レスポンスを連結:後続のリクエストで使う値をレスポンスから取得します
  7. バージョン管理:.http ファイルをリポジトリにコミットします
  8. 設定を共有:.vscode/settings.json をリポジトリに含めます

出力チェックリスト

  • すべてのルートを HTTP リクエストに変換
  • パスパラメーターで {{param}} 構文を使用
  • POST/PUT/PATCH のリクエストボディを含める
  • ヘッダーを設定(Content-Type、Authorization)
  • settings.json で環境変数を定義
  • リクエストをリソースごと、または単一ファイルに整理
  • レスポンス連結用の名前付きリクエスト
  • リクエストボディにサンプルデータを記載
  • 各リクエストにコメント/説明を追加

コメント