Created
June 12, 2025 16:35
-
-
Save gabigabogabu/0f7fa9ee04b63dcc48cd9dd74681370e to your computer and use it in GitHub Desktop.
postman 2 curl
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env node | |
import { readFile, writeFile, mkdir } from 'fs/promises'; | |
import { join } from 'path'; | |
/** | |
* @typedef {Object} PostmanUrl | |
* @property {string} [raw] | |
* @property {string} [protocol] | |
* @property {string[]} [host] | |
* @property {string} [port] | |
* @property {string[]} [path] | |
* @property {Array<{key: string, value: string, disabled?: boolean}>} [query] | |
*/ | |
/** | |
* @typedef {Object} PostmanHeader | |
* @property {string} key | |
* @property {string} value | |
* @property {string} [type] | |
* @property {boolean} [disabled] | |
*/ | |
/** | |
* @typedef {Object} PostmanBody | |
* @property {string} [mode] | |
* @property {string} [raw] | |
* @property {Object} [options] | |
* @property {Object} [options.raw] | |
* @property {string} [options.raw.language] | |
*/ | |
/** | |
* @typedef {Object} PostmanAuth | |
* @property {string} type | |
* @property {Array<{key: string, value: string, type: string}>} [basic] | |
* @property {Array<{key: string, value: string, type: string}>} [bearer] | |
* @property {Array<{key: string, value: string, type: string}>} [apikey] | |
*/ | |
/** | |
* @typedef {Object} PostmanRequest | |
* @property {string} method | |
* @property {PostmanHeader[]} [header] | |
* @property {PostmanBody} [body] | |
* @property {PostmanUrl} [url] | |
* @property {PostmanAuth} [auth] | |
*/ | |
/** | |
* @typedef {Object} PostmanItem | |
* @property {string} name | |
* @property {PostmanRequest} [request] | |
* @property {PostmanItem[]} [item] | |
* @property {any[]} [response] | |
*/ | |
/** | |
* @typedef {Object} PostmanCollection | |
* @property {Object} info | |
* @property {string} info.name | |
* @property {string} info._postman_id | |
* @property {string} info.schema | |
* @property {PostmanItem[]} item | |
*/ | |
/** | |
* Load a Postman collection from file | |
* @param {string} collectionPath | |
* @returns {Promise<PostmanCollection>} | |
*/ | |
async function loadCollection(collectionPath) { | |
try { | |
const fileContent = await readFile(collectionPath, 'utf-8'); | |
const collection = JSON.parse(fileContent); | |
return collection; | |
} catch (error) { | |
throw new Error(`Failed to load collection: ${error}`); | |
} | |
} | |
/** | |
* Build URL from Postman URL object | |
* @param {PostmanUrl | undefined} url | |
* @returns {string} | |
*/ | |
function buildUrl(url) { | |
if (!url) return 'http://localhost'; | |
if (url.raw) return url.raw; | |
let fullUrl = ''; | |
if (url.protocol) | |
fullUrl += `${url.protocol}://`; | |
else | |
fullUrl += 'http://'; | |
if (url.host && url.host.length > 0) | |
fullUrl += url.host.join('.'); | |
else | |
fullUrl += 'localhost'; | |
if (url.port) fullUrl += `:${url.port}`; | |
if (url.path && url.path.length > 0) | |
fullUrl += `/${url.path.join('/')}`; | |
if (url.query && url.query.length > 0) { | |
const enabledQueries = url.query.filter(q => !q.disabled); | |
if (enabledQueries.length > 0) { | |
const queryString = enabledQueries | |
.map(q => `${encodeURIComponent(q.key)}=${encodeURIComponent(q.value)}`) | |
.join('&'); | |
fullUrl += `?${queryString}`; | |
} | |
} | |
return fullUrl; | |
} | |
/** | |
* Build headers array for curl command | |
* @param {PostmanHeader[]} [headers] | |
* @returns {string[]} | |
*/ | |
function buildHeaders(headers) { | |
if (!headers) return []; | |
return headers | |
.filter(header => !header.disabled) | |
.map(header => `-H "${header.key}: ${header.value.replace(/"/g, '\\"')}"`) | |
.filter(header => header.length > 0); | |
} | |
/** | |
* Build body string for curl command | |
* @param {PostmanBody} [body] | |
* @returns {string} | |
*/ | |
function buildBody(body) { | |
if (!body || !body.raw) return ''; | |
const escapedBody = body.raw | |
.replace(/\\/g, '\\\\') | |
.replace(/"/g, '\\"') | |
.replace(/\n/g, '\\n') | |
.replace(/\r/g, '\\r') | |
.replace(/\t/g, '\\t'); | |
return `-d "${escapedBody}"`; | |
} | |
/** | |
* Build authentication parts for curl command | |
* @param {PostmanAuth} [auth] | |
* @returns {string[]} | |
*/ | |
function buildAuth(auth) { | |
if (!auth) return []; | |
const authParts = []; | |
switch (auth.type) { | |
case 'basic': | |
if (auth.basic) { | |
const username = auth.basic.find(item => item.key === 'username')?.value || ''; | |
const password = auth.basic.find(item => item.key === 'password')?.value || ''; | |
if (username || password) { | |
authParts.push(`-u "${username}:${password}"`); | |
} | |
} | |
break; | |
case 'bearer': | |
if (auth.bearer) { | |
const token = auth.bearer.find(item => item.key === 'token')?.value; | |
if (token) { | |
authParts.push(`-H "Authorization: Bearer ${token}"`); | |
} | |
} | |
break; | |
case 'apikey': | |
if (auth.apikey) { | |
const key = auth.apikey.find(item => item.key === 'key')?.value; | |
const value = auth.apikey.find(item => item.key === 'value')?.value; | |
const addTo = auth.apikey.find(item => item.key === 'in')?.value || 'header'; | |
if (key && value) { | |
if (addTo === 'header') { | |
authParts.push(`-H "${key}: ${value}"`); | |
} | |
// Note: Query parameter API keys would need URL modification | |
} | |
} | |
break; | |
case 'noauth': | |
default: | |
// No authentication needed | |
break; | |
} | |
return authParts; | |
} | |
/** | |
* Convert a Postman request to curl command | |
* @param {PostmanRequest} request | |
* @param {string} name | |
* @returns {string} | |
*/ | |
function requestToCurl(request, name) { | |
const url = buildUrl(request.url); | |
const method = request.method.toUpperCase(); | |
const headers = buildHeaders(request.header); | |
const auth = buildAuth(request.auth); | |
const body = buildBody(request.body); | |
let curlCommand = `# ${name}\ncurl`; | |
if (method !== 'GET') | |
curlCommand += ` -X ${method}`; | |
if (auth.length > 0) | |
curlCommand += ` \\\n ${auth.join(' \\\n ')}`; | |
if (headers.length > 0) | |
curlCommand += ` \\\n ${headers.join(' \\\n ')}`; | |
if (body && ['POST', 'PUT', 'PATCH'].includes(method)) | |
curlCommand += ` \\\n ${body}`; | |
curlCommand += ` \\\n "${url}"`; | |
return curlCommand; | |
} | |
/** | |
* Extract all requests from collection items recursively with folder structure | |
* @param {PostmanItem[]} items | |
* @param {string} [parentPath] | |
* @returns {Array<{name: string, request: PostmanRequest, path: string}>} | |
*/ | |
function extractRequests(items, parentPath = '') { | |
return items.reduce((acc, item) => { | |
const currentPath = parentPath ? `${parentPath}/${item.name}` : item.name; | |
if (item.request) acc.push({ name: item.name, request: item.request, path: currentPath }); | |
if (item.item) acc.push(...extractRequests(item.item, currentPath)); | |
return acc; | |
}, []); | |
} | |
/** | |
* Sanitize filename by removing invalid characters | |
* @param {string} filename | |
* @returns {string} | |
*/ | |
function sanitizeFilename(filename) { | |
return filename | |
.replace(/[<>:"/\\|?*]/g, '_') | |
.replace(/\s+/g, '_') | |
.replace(/_{2,}/g, '_') | |
.replace(/^_|_$/g, ''); | |
} | |
/** | |
* Create directory structure and write individual request files | |
* @param {PostmanCollection} collection | |
* @param {string} outputDir | |
* @returns {Promise<void>} | |
*/ | |
async function saveRequestsToFiles(collection, outputDir) { | |
const requests = extractRequests(collection.item); | |
if (requests.length === 0) { | |
console.log('No requests found in the collection'); | |
return; | |
} | |
// Create the main output directory | |
await mkdir(outputDir, { recursive: true }); | |
// Create a summary file | |
const summaryContent = `# ${collection.info.name} | |
# Total requests: ${requests.length} | |
# Generated on: ${new Date().toISOString()} | |
`; | |
await writeFile(join(outputDir, 'README.md'), summaryContent, 'utf-8'); | |
let savedCount = 0; | |
for (const { name, request, path } of requests) { | |
try { | |
const pathParts = path.split('/'); | |
const filename = sanitizeFilename(pathParts.pop()) + '.sh'; | |
const dirPath = pathParts.length > 0 ? join(outputDir, ...pathParts.map(sanitizeFilename)) : outputDir; | |
if (pathParts.length > 0) | |
await mkdir(dirPath, { recursive: true }); | |
const curlCommand = requestToCurl(request, name); | |
const fileContent = `#!/bin/bash | |
# ${name} | |
# Generated on: ${new Date().toISOString()} | |
${curlCommand} | |
`; | |
const filePath = join(dirPath, filename); | |
await writeFile(filePath, fileContent, 'utf-8'); | |
savedCount++; | |
} catch (error) { | |
console.error(`Error saving request "${name}": ${error.message}`); | |
} | |
} | |
console.log(`Saved ${savedCount} curl commands to ${outputDir}/`); | |
} | |
/** | |
* Main execution function | |
* @returns {Promise<void>} | |
*/ | |
async function main() { | |
const args = process.argv.slice(2); | |
if (args.length === 0) { | |
console.log(` | |
🔄 Postman to Curl Converter | |
Usage: | |
node pm2curl.js <collection-file> [output-directory] | |
Examples: | |
node pm2curl.js collection.json | |
node pm2curl.js collection.json ./curl-commands | |
node pm2curl.js example.postman_collection.json | |
Options: | |
collection-file Path to the Postman collection JSON file | |
output-directory Output directory for curl files (defaults to collection-name-curl) | |
`); | |
process.exit(1); | |
} | |
const collectionFile = args[0]; | |
const outputDir = args[1] || collectionFile.replace(/\.json$/, '').replace(/.*\//, '') + '-curl'; | |
try { | |
const collection = await loadCollection(collectionFile); | |
await saveRequestsToFiles(collection, outputDir); | |
} catch (error) { | |
console.error(`Error: ${error instanceof Error ? error.message : error}`); | |
process.exit(1); | |
} | |
} | |
// Run the main function if this script is executed directly | |
if (process.argv[1] === new URL(import.meta.url).pathname) { | |
main(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment