vscode-rest-client-generator について
APIルートからVS Code REST Client用の`.http`ファイルを生成する開発者向けスキルです。リクエスト整理、環境変数、認証、サンプル本文、レスポンス連携を扱います。
主な機能
- 01APIルート情報の抽出
- 02整理された`.http`リクエストファイル
- 03環境別変数
- 04Bearer、Basic、API Key認証
- 05レスポンス変数の連携
- 06GraphQLリクエスト例
活用シーン
- 01VS Code内のAPIテストを準備
- 02環境ごとにリクエストを切り替え
- 03後続リクエストでログイン応答を再利用
- 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 ファイルを生成します。
基本的なワークフロー
- ルートをスキャン:すべての API ルート定義を検索します
- メタデータを抽出:メソッド、パス、パラメーター、ボディを抽出します
- .http ファイルを作成:リソースごと、または単一ファイルに整理します
- 変数を追加:環境ごとの値を設定します
- 認証を設定:Bearer、Basic、API Key
- サンプルを含める:サンプルデータを使ったリクエストボディを含めます
ファイル構成のオプション
# 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();
ベストプラクティス
- 名前付きリクエストを使用:変数を取得するには
# @name requestNameを追加します - リソースごとに整理:API リソースごとに .http ファイルを 1 つ作成します
- 環境を切り替え:設定で複数の環境を構成します
- サンプルを含める:現実的なサンプルデータをあらかじめボディに入力します
- コメントを追加:各リクエストの目的を記載します
- レスポンスを連結:後続のリクエストで使う値をレスポンスから取得します
- バージョン管理:.http ファイルをリポジトリにコミットします
- 設定を共有:.vscode/settings.json をリポジトリに含めます
出力チェックリスト
- すべてのルートを HTTP リクエストに変換
- パスパラメーターで
{{param}}構文を使用 - POST/PUT/PATCH のリクエストボディを含める
- ヘッダーを設定(Content-Type、Authorization)
- settings.json で環境変数を定義
- リクエストをリソースごと、または単一ファイルに整理
- レスポンス連結用の名前付きリクエスト
- リクエストボディにサンプルデータを記載
- 各リクエストにコメント/説明を追加
コメント
Markdown 対応