vscode-rest-client-generator
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseVS 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
核心工作流程
- Scan routes: Find all API route definitions
- Extract metadata: Methods, paths, params, bodies
- Create .http files: Organize by resource or single file
- Add variables: Environment-specific values
- Configure auth: Bearer, Basic, API Key
- Include examples: Request bodies with sample data
- 扫描路由:查找所有API路由定义
- 提取元数据:请求方法、路径、参数、请求体
- 创建.http文件:按资源分类或生成单个文件
- 添加变量:环境专属配置值
- 配置认证:Bearer、Basic、API Key
- 包含示例:带示例数据的请求体
File Structure Options
文件结构选项
undefinedundefinedOption 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
undefinedhttp/
├── local.http
├── staging.http
└── production.http
undefinedBasic .http File Syntax
基础.http文件语法
http
undefinedhttp
undefinedGet 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}}
undefinedDELETE {{baseUrl}}/users/{{userId}}
Authorization: Bearer {{authToken}}
undefinedEnvironment Variables
环境变量
http
undefinedhttp
undefinedsettings.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": {
"baseUrl": "http://localhost:3000/api",
"baseUrl": "http://localhost:3000/api",
"authToken": "local-dev-token"
"authToken": "local-dev-token"
},
},
"staging": {
"staging": {
"baseUrl": "https://staging-api.example.com",
"baseUrl": "https://staging-api.example.com",
"authToken": ""
"authToken": ""
},
},
"production": {
"production": {
"baseUrl": "https://api.example.com",
"baseUrl": "https://api.example.com",
"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
undefinedundefinedGenerator 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
undefinedhttp
undefinedUsers 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--
undefinedPOST {{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--
undefinedauth.http
auth.http
http
undefinedhttp
undefinedAuthentication 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"
}
undefinedPOST {{baseUrl}}/auth/reset-password
Content-Type: application/json
{
"token": "{{resetToken}}",
"password": "newpassword123"
}
undefinedAdvanced Features
高级功能
Response Variables
响应变量
http
undefinedhttp
undefinedLogin 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}}
undefinedGET {{baseUrl}}/users/{{userId}}
Authorization: Bearer {{token}}
undefinedDynamic Variables
动态变量
http
undefinedhttp
undefinedCreate 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
undefinedundefinedGraphQL Requests
GraphQL请求
http
undefinedhttp
undefinedGraphQL 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"
}
}
}
undefinedPOST {{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"
}
}
}
undefinedVS 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
最佳实践
- Use named requests: Add for variable capture
# @name requestName - Organize by resource: One .http file per API resource
- Environment switching: Configure multiple environments in settings
- Include examples: Pre-fill bodies with realistic sample data
- Add comments: Document each request's purpose
- Response chaining: Capture values from responses for subsequent requests
- Version control: Commit .http files to repository
- Share settings: Include .vscode/settings.json in repo
- 使用命名请求:添加用于变量捕获
# @name requestName - 按资源分类:每个API资源对应一个.http文件
- 环境切换:在设置中配置多套环境
- 包含示例:为请求体填充真实的示例数据
- 添加注释:记录每个请求的用途
- 响应链式调用:从响应中捕获值用于后续请求
- 版本控制:将.http文件提交到代码仓库
- 共享设置:将.vscode/settings.json纳入仓库管理
Output Checklist
输出检查清单
- All routes converted to HTTP requests
- Path parameters use syntax
{{param}} - 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中定义环境变量
- 请求按资源分类或生成在单个文件中
- 已添加命名请求用于响应链式调用
- 请求体中包含示例数据
- 每个请求都有注释/描述