vscode-rest-client-generator
SKILL.md
VS Code REST Client Generator
Generate .http files for inline API testing in VS Code without leaving the editor.
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
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": "john@example.com"
}
### 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": "john@example.com",
"role": "user"
}
### Update user
# @name updateUser
PUT {{baseUrl}}/users/{{userId}}
Content-Type: application/json
Authorization: Bearer {{authToken}}
{
"name": "John Updated",
"email": "john.updated@example.com"
}
### 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": "user@example.com",
"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": "newuser@example.com",
"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": "user@example.com"
}
### 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": "user@example.com",
"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": "new@example.com"
}
}
}
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
- Use named requests: Add
# @name requestNamefor variable capture - 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
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
Weekly Installs
10
Repository
patricio0312rev/skillsFirst Seen
10 days ago
Installed on
claude-code8
gemini-cli7
antigravity7
windsurf7
github-copilot7
codex7