vscode-rest-client-generator

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

VS Code REST Client Generator

VS Code REST Client 生成器

Generate .http files for inline API testing in VS Code without leaving the editor.
无需离开编辑器,即可为VS Code中的内嵌API测试生成.http文件。

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
  1. 扫描路由:查找所有API路由定义
  2. 提取元数据:请求方法、路径、参数、请求体
  3. 创建.http文件:按资源分类或生成单个文件
  4. 添加变量:环境专属配置值
  5. 配置认证:Bearer、Basic、API Key
  6. 包含示例:带示例数据的请求体

File Structure Options

文件结构选项

undefined
undefined

Option 1: Single file

Option 1: Single file

api-requests.http
api-requests.http

Option 2: By resource

Option 2: By resource

http/ ├── users.http ├── products.http ├── orders.http └── auth.http
http/ ├── users.http ├── products.http ├── orders.http └── auth.http

Option 3: By environment

Option 3: By environment

http/ ├── local.http ├── staging.http └── production.http
undefined
http/ ├── local.http ├── staging.http └── production.http
undefined

Basic .http File Syntax

基础.http文件语法

http
undefined
http
undefined

Get all users

Get all users

GET {{baseUrl}}/users Authorization: Bearer {{authToken}}
GET {{baseUrl}}/users Authorization: Bearer {{authToken}}

Get user by ID

Get user by ID

GET {{baseUrl}}/users/{{userId}} Authorization: Bearer {{authToken}}
GET {{baseUrl}}/users/{{userId}} Authorization: Bearer {{authToken}}

Create user

Create user

POST {{baseUrl}}/users Content-Type: application/json Authorization: Bearer {{authToken}}
{ "name": "John Doe", "email": "john@example.com" }
POST {{baseUrl}}/users Content-Type: application/json Authorization: Bearer {{authToken}}
{ "name": "John Doe", "email": "john@example.com" }

Update user

Update user

PUT {{baseUrl}}/users/{{userId}} Content-Type: application/json Authorization: Bearer {{authToken}}
{ "name": "John Updated" }
PUT {{baseUrl}}/users/{{userId}} Content-Type: application/json Authorization: Bearer {{authToken}}
{ "name": "John Updated" }

Delete user

Delete user

DELETE {{baseUrl}}/users/{{userId}} Authorization: Bearer {{authToken}}
undefined
DELETE {{baseUrl}}/users/{{userId}} Authorization: Bearer {{authToken}}
undefined

Environment Variables

环境变量

http
undefined
http
undefined

settings.json or .vscode/settings.json

settings.json or .vscode/settings.json

{

{

"rest-client.environmentVariables": {

"rest-client.environmentVariables": {

"$shared": {

"$shared": {

"version": "v1"

"version": "v1"

},

},

"local": {

"local": {

"authToken": "local-dev-token"

"authToken": "local-dev-token"

},

},

"staging": {

"staging": {

"authToken": ""

"authToken": ""

},

},

"production": {

"production": {

"authToken": ""

"authToken": ""

}

}

}

}

}

}

Variables Reference

Variables Reference

Use Ctrl+Alt+E (Cmd+Alt+E on Mac) to switch environments

Use Ctrl+Alt+E (Cmd+Alt+E on Mac) to switch environments

{{$shared.version}} - shared across all environments

{{$shared.version}} - shared across all environments

{{baseUrl}} - from current environment

{{baseUrl}} - from current environment

{{$timestamp}} - current timestamp

{{$timestamp}} - current timestamp

{{$randomInt min max}} - random integer

{{$randomInt min max}} - random integer

{{$guid}} - random UUID

{{$guid}} - random UUID

undefined
undefined

Generator Script

生成器脚本

typescript
// 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;
}
typescript
// 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.http

http
undefined
http
undefined

Users API

Users API

Environment: {{$env}}

Environment: {{$env}}

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

List all users

List all users

@name listUsers

@name listUsers

GET {{baseUrl}}/users?page=1&limit=10 Authorization: Bearer {{authToken}}
GET {{baseUrl}}/users?page=1&limit=10 Authorization: Bearer {{authToken}}

Get user by ID

Get user by ID

@name getUser

@name getUser

GET {{baseUrl}}/users/{{userId}} Authorization: Bearer {{authToken}}
GET {{baseUrl}}/users/{{userId}} Authorization: Bearer {{authToken}}

Create new user

Create new user

@name createUser

@name createUser

POST {{baseUrl}}/users Content-Type: application/json Authorization: Bearer {{authToken}}
{ "name": "John Doe", "email": "john@example.com", "role": "user" }
POST {{baseUrl}}/users Content-Type: application/json Authorization: Bearer {{authToken}}
{ "name": "John Doe", "email": "john@example.com", "role": "user" }

Update user

Update user

@name updateUser

@name updateUser

PUT {{baseUrl}}/users/{{userId}} Content-Type: application/json Authorization: Bearer {{authToken}}
{ "name": "John Updated", "email": "john.updated@example.com" }
PUT {{baseUrl}}/users/{{userId}} Content-Type: application/json Authorization: Bearer {{authToken}}
{ "name": "John Updated", "email": "john.updated@example.com" }

Partial update user

Partial update user

@name patchUser

@name patchUser

PATCH {{baseUrl}}/users/{{userId}} Content-Type: application/json Authorization: Bearer {{authToken}}
{ "status": "active" }
PATCH {{baseUrl}}/users/{{userId}} Content-Type: application/json Authorization: Bearer {{authToken}}
{ "status": "active" }

Delete user

Delete user

@name deleteUser

@name deleteUser

DELETE {{baseUrl}}/users/{{userId}} Authorization: Bearer {{authToken}}
DELETE {{baseUrl}}/users/{{userId}} Authorization: Bearer {{authToken}}

Upload user avatar

Upload user avatar

@name uploadAvatar

@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--
undefined
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--
undefined

auth.http

auth.http

http
undefined
http
undefined

Authentication API

Authentication API

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

Login

Login

@name login

@name login

POST {{baseUrl}}/auth/login Content-Type: application/json
{ "email": "user@example.com", "password": "password123" }
POST {{baseUrl}}/auth/login Content-Type: application/json
{ "email": "user@example.com", "password": "password123" }

Use token from login response

Use token from login response

@authToken = {{login.response.body.$.token}}
@authToken = {{login.response.body.$.token}}

Register

Register

@name register

@name register

POST {{baseUrl}}/auth/register Content-Type: application/json
{ "name": "New User", "email": "newuser@example.com", "password": "securepassword123" }
POST {{baseUrl}}/auth/register Content-Type: application/json
{ "name": "New User", "email": "newuser@example.com", "password": "securepassword123" }

Refresh token

Refresh token

POST {{baseUrl}}/auth/refresh Content-Type: application/json Authorization: Bearer {{authToken}}
{ "refreshToken": "{{refreshToken}}" }
POST {{baseUrl}}/auth/refresh Content-Type: application/json Authorization: Bearer {{authToken}}
{ "refreshToken": "{{refreshToken}}" }

Logout

Logout

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

Forgot password

Forgot password

POST {{baseUrl}}/auth/forgot-password Content-Type: application/json
{ "email": "user@example.com" }
POST {{baseUrl}}/auth/forgot-password Content-Type: application/json
{ "email": "user@example.com" }

Reset password

Reset password

POST {{baseUrl}}/auth/reset-password Content-Type: application/json
{ "token": "{{resetToken}}", "password": "newpassword123" }
undefined
POST {{baseUrl}}/auth/reset-password Content-Type: application/json
{ "token": "{{resetToken}}", "password": "newpassword123" }
undefined

Advanced Features

高级功能

Response Variables

响应变量

http
undefined
http
undefined

Login and capture token

Login and capture token

@name login

@name login

POST {{baseUrl}}/auth/login Content-Type: application/json
{ "email": "user@example.com", "password": "password123" }
POST {{baseUrl}}/auth/login Content-Type: application/json
{ "email": "user@example.com", "password": "password123" }

Use captured token

Use captured token

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

Make authenticated request

Make authenticated request

GET {{baseUrl}}/users/{{userId}} Authorization: Bearer {{token}}
undefined
GET {{baseUrl}}/users/{{userId}} Authorization: Bearer {{token}}
undefined

Dynamic Variables

动态变量

http
undefined
http
undefined

Create with random data

Create with random data

POST {{baseUrl}}/users Content-Type: application/json
{ "id": "{{$guid}}", "email": "user-{{$randomInt 1000 9999}}@example.com", "createdAt": "{{$timestamp}}" }
POST {{baseUrl}}/users Content-Type: application/json
{ "id": "{{$guid}}", "email": "user-{{$randomInt 1000 9999}}@example.com", "createdAt": "{{$timestamp}}" }

Available dynamic variables:

Available dynamic variables:

{{$guid}} - UUID v4

{{$guid}} - UUID v4

{{$randomInt min max}} - random integer

{{$randomInt min max}} - random integer

{{$timestamp}} - Unix timestamp

{{$timestamp}} - Unix timestamp

{{$timestamp offset option}} - with offset

{{$timestamp offset option}} - with offset

{{$datetime rfc1123}} - formatted date

{{$datetime rfc1123}} - formatted date

{{$localDatetime iso8601}} - local datetime

{{$localDatetime iso8601}} - local datetime

{{$processEnv VAR_NAME}} - environment variable

{{$processEnv VAR_NAME}} - environment variable

undefined
undefined

GraphQL Requests

GraphQL请求

http
undefined
http
undefined

GraphQL Query

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

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": "new@example.com" } } }
undefined
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": "new@example.com" } } }
undefined

VS Code Settings

VS Code设置

json
// .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"
  }
}
json
// .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

CLI脚本

typescript
#!/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();
typescript
#!/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
  1. 使用命名请求:添加
    # @name requestName
    用于变量捕获
  2. 按资源分类:每个API资源对应一个.http文件
  3. 环境切换:在设置中配置多套环境
  4. 包含示例:为请求体填充真实的示例数据
  5. 添加注释:记录每个请求的用途
  6. 响应链式调用:从响应中捕获值用于后续请求
  7. 版本控制:将.http文件提交到代码仓库
  8. 共享设置:将.vscode/settings.json纳入仓库管理

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
  • 所有路由已转换为HTTP请求
  • 路径参数使用
    {{param}}
    语法
  • POST/PUT/PATCH请求已包含请求体
  • 已配置请求头(Content-Type、Authorization)
  • 已在settings.json中定义环境变量
  • 请求按资源分类或生成在单个文件中
  • 已添加命名请求用于响应链式调用
  • 请求体中包含示例数据
  • 每个请求都有注释/描述