api-explorer-cli
Version:
A CLI tool to proxy localhost API requests for the API Explorer web app
155 lines (138 loc) • 4.39 kB
JavaScript
import express from 'express';
import axios from 'axios';
import cors from 'cors';
import { URLSearchParams } from 'url';
const app = express();
const port = 9999;
// Middleware to enable CORS
app.use(cors({
origin: 'https://apiexplorer.vercel.app', // Replace with your Vercel domain
}));
app.use(express.json());
// Proxy endpoint to forward requests to localhost
app.post('/proxy', async (req, res) => {
const {
method,
url,
headers = [],
params = [],
body,
bodyType,
authType,
authData = {},
} = req.body;
// Validate localhost URL
if (!url || !url.startsWith('http://localhost')) {
return res.status(400).json({
statusCode: 400,
statusText: 'Bad Request',
responseTime: 0,
headers: {},
body: 'Invalid or non-localhost URL provided',
size: 0,
});
}
try {
// Prepare headers
const requestHeaders = {};
headers
.filter((header) => header.enabled)
.forEach((header) => {
requestHeaders[header.name] = header.value;
});
// Add authentication headers
if (authType === 'bearer' && authData.token) {
requestHeaders['Authorization'] = `Bearer ${authData.token}`;
} else if (authType === 'basic' && authData.username && authData.password) {
requestHeaders['Authorization'] = `Basic ${Buffer.from(
`${authData.username}:${authData.password}`
).toString('base64')}`;
}
// Prepare query parameters
const requestParams = {};
params
.filter((param) => param.enabled)
.forEach((param) => {
requestParams[param.name] = param.value;
});
// Prepare request data based on bodyType
let requestData;
if (['POST', 'PUT', 'PATCH'].includes(method.toUpperCase())) {
switch (bodyType) {
case 'form-data':
requestData = body;
delete requestHeaders['Content-Type'];
break;
case 'x-www-form-urlencoded':
requestData = new URLSearchParams();
body.forEach((field) => {
requestData.append(field.key, field.value);
});
requestHeaders['Content-Type'] = 'application/x-www-form-urlencoded';
break;
case 'raw':
requestData = body;
break;
case 'GraphQL':
requestData = body;
requestHeaders['Content-Type'] = 'application/json';
break;
case 'binary':
requestData = body;
requestHeaders['Content-Type'] = 'application/octet-stream';
break;
default:
requestData = undefined;
}
}
// Axios configuration
const config = {
method: method.toUpperCase(),
url,
headers: requestHeaders,
params: requestParams,
data: requestData,
timeout: 30000,
validateStatus: () => true,
};
// Measure response time
const startTime = Date.now();
const response = await axios(config);
const responseTime = Date.now() - startTime;
// Calculate response size
const responseBody = response.data;
const size = Buffer.byteLength(
typeof responseBody === 'string' ? responseBody : JSON.stringify(responseBody)
);
// Prepare response
const responseData = {
statusCode: response.status,
statusText: response.statusText,
responseTime,
headers: response.headers,
body:
typeof responseBody === 'string'
? responseBody
: JSON.stringify(responseBody, null, 2),
size,
};
res.json(responseData);
} catch (error) {
console.error('Proxy request failed:', error);
const errorResponse = {
statusCode: error.response?.status || 500,
statusText: error.response?.statusText || 'Internal Server Error',
responseTime: 0,
headers: error.response?.headers || {},
body: error.message || 'Proxy request failed',
size: 0,
};
res.status(errorResponse.statusCode).json(errorResponse);
}
});
// Start the proxy server
app.listen(port, () => {
console.log(`API Explorer Proxy running at http://localhost:${port}`);
console.log('Use this in your API Explorer to forward localhost requests.');
});