mcpField
V
vscode-rest-client-generator

vscode-rest-client-generator

github.com/patricio0312revTesting & quality63246 installsUpdated 41w ago
Repository

About vscode-rest-client-generator

A developer skill for generating VS Code REST Client `.http` files from API routes. It covers request organization, environment variables, authentication, sample bodies, and response chaining.

Key features

  1. 01API route metadata extraction
  2. 02Organized `.http` request files
  3. 03Environment-specific variables
  4. 04Bearer, Basic, and API Key auth
  5. 05Response variable chaining
  6. 06GraphQL request examples

Use cases

  1. 01Prepare inline API tests in VS Code
  2. 02Switch requests across environments
  3. 03Reuse login responses in later requests
  4. 04Test GraphQL endpoints from the editor

Install

Copy the command and run it in a terminal.

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

After installing, type/vscode-rest-client-generatorin Claude Code to confirm it loaded.

Details

From the project README

VS Code REST Client Generator

Generate .http files for inline API testing in VS Code without leaving the editor.

Core Workflow

  1. Scan routes: Find all API route definitions
  2. Extract metadata: Methods, paths, params, bodies
  3. Create .http files: Organize by resource or single file
  4. Add variables: Environment-specific values
  5. Configure auth: Bearer, Basic, API Key
  6. Include examples: Request bodies with sample data

File Structure Options

# 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

Basic .http File Syntax

### 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}}

Environment Variables

# 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

Generator Script

// 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;
}

Complete Example Files

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"
}

Advanced Features

Response Variables
### 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}}
Dynamic Variables
### 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 Requests
### 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 Settings

// .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 Script

#!/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();

Best Practices

  1. Use named requests: Add # @name requestName for variable capture
  2. Organize by resource: One .http file per API resource
  3. Environment switching: Configure multiple environments in settings
  4. Include examples: Pre-fill bodies with realistic sample data
  5. Add comments: Document each request's purpose
  6. Response chaining: Capture values from responses for subsequent requests
  7. Version control: Commit .http files to repository
  8. Share settings: Include .vscode/settings.json in repo

Output Checklist

  • All routes converted to HTTP requests
  • Path parameters use {{param}} syntax
  • Request bodies included for POST/PUT/PATCH
  • Headers configured (Content-Type, Authorization)
  • Environment variables defined in settings.json
  • Requests organized by resource or in single file
  • Named requests for response chaining
  • Sample data in request bodies
  • Comments/descriptions for each request

Comments

Markdown supported