In a previous post, I wrote about a tool I built to generate .http files from OpenAPI specifications. This came out of necessity as the API’s I work with have very large payloads by design and SwaggerUI really doesn’t like that. I work with API’s that implement standardized communication protocols that are not subject to change. I love learning, and learning things outside my usual tech stack can be a lot of fun. Over the christmas holidays, I thought of learning a different programming workflow using text based editors, like NeoVim and staying in the terminal without interacting with the mouse.

I spend most of my time in a modern IDE, specifically Jetbrains Rider, so working in a text-based editor felt very unproductive to me. I’m a big fan of using different view modes in Rider, particularly, the Zen Mode, which technically isn’t that far from a text-based editor, since all I see is, well, text. Productivity wasn’t the point of this exercise, the point was to learn a new coding workflow. I tried building a .NET based API and when testing it out I wanted stay purely on the terminal and didn’t want to switch to a browser to run SwaggerUI or to send requests through Postman.

I’ve used cURL in the past, and for basic requests without a body, this was simple enough and looks something like this:

curl -X GET https://petstore3.swagger.io/api/v3/pet/1 -H 'Accept: application/json' -H 'Content-Type: application/json'

Very soon, I needed to test more complex endpoints that had a request body. This is tedious to do by hand using cURL, and tools like SwaggerUI and Postman are generally good at generating cURL requests. A cURL request with a request body looks something like this:

curl -X POST https://petstore3.swagger.io/api/v3/pet -H 'Accept: application/json' -H 'Content-Type: application/json' -d '{
  "id": 0,
  "name": "name",
  "category": {
    "id": 0,
    "name": "name"
  },
  "photoUrls": [
    ""
  ],
  "tags": [
    {
      "id": 0,
      "name": "name"
    }
  ],
  "status": "available"
}'

This got very tedious very fast and being the lazy person that I am, I thought to myself, “I can build a tool to do this for me”. After some hours and a couple of espresso shots, the cURL Request Generator was born. This tool was basically a fork of HTTP File Generator which had everything I needed to parse OpenAPI specifications and build cURL requests. I switch between using MacOS and Windows several times a day, and my scripting language of choice is Powershell Core since is cross platform and can use .NET types directly.

The first cURL request I generated looked something like this powershell script:

<#
  Request: GET /pet/{petId}
  Summary: Find pet by ID
  Description: Returns a single pet
#>
param(
   <# ID of pet to return #>
   [Parameter(Mandatory=$True)]
   [String] $petId
)

curl -X GET https://petstore3.swagger.io/api/v3/pet/$petId `
  -H 'Accept: application/json'

This allows me to run scripts like GetPetById.ps1 and provide route parameters as powershell script parameters. Using cURL Request Generator, I can generate complex requests wrapped in powershell scripts like this:

<#
  Request: POST /pet
  Summary: Add a new pet to the store
  Description: Add a new pet to the store
#>

curl -X POST https://petstore3.swagger.io/api/v3/pet `
  -H 'Accept: application/json' `
  -H 'Content-Type: application/json' `
  -d '{
  "id": 0,
  "name": "name",
  "category": {
    "id": 0,
    "name": "name"
  },
  "photoUrls": [
    ""
  ],
  "tags": [
    {
      "id": 0,
      "name": "name"
    }
  ],
  "status": "available"
}'

cURL Request Generator is distributed as a .NET Tool via NuGet.org.

To install, simply use the following command

dotnet tool install --global curlgenerator

The tool provides some usage instructions and examples when running

curlgenerator --help
USAGE:
    curlgenerator [URL or input file] [OPTIONS]

EXAMPLES:
    curlgenerator ./openapi.json
    curlgenerator ./openapi.json --output ./
    curlgenerator https://petstore.swagger.io/v2/swagger.json
    curlgenerator https://petstore3.swagger.io/api/v3/openapi.json --base-url https://petstore3.swagger.io
    curlgenerator ./openapi.json --authorization-header Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
    curlgenerator ./openapi.json --azure-scope [Some Application ID URI]/.default

ARGUMENTS:
    [URL or input file]    URL or file path to OpenAPI Specification file

OPTIONS:
                                           DEFAULT                                                                                                                            
    -h, --help                                                  Prints help information                                                                                       
    -v, --version                                               Prints version information                                                                                    
    -o, --output <OUTPUT>                  ./                   Output directory                                                                                              
        --no-logging                                            Don't log errors or collect telemetry                                                                         
        --skip-validation                                       Skip validation of OpenAPI Specification file                                                                 
        --authorization-header <HEADER>                         Authorization header to use for all requests                                                                  
        --content-type <CONTENT-TYPE>      application/json     Default Content-Type header to use for all requests                                                           
        --base-url <BASE-URL>                                   Default Base URL to use for all requests. Use this if the OpenAPI spec doesn't explicitly specify a server URL
        --azure-scope <SCOPE>                                   Azure Entra ID Scope to use for retrieving Access Token for Authorization header                              
        --azure-tenant-id <TENANT-ID>                           Azure Entra ID Tenant ID to use for retrieving Access Token for Authorization header                          

An example usage would be something like this:

curlgenerator https://petstore3.swagger.io/v2/swagger.json

which outputs the following:

cURL Request Generator v0.1.1
Support key: mbmbqvd

OpenAPI statistics:
 - Path Items: 14
 - Operations: 20
 - Parameters: 14
 - Request Bodies: 9
 - Responses: 20
 - Links: 0
 - Callbacks: 0
 - Schemas: 67

Files: 20
Duration: 00:00:02.3089450

and produces the following files:

-rw-r--r-- 1 christian 197121  593 Dec 10 10:44 DeleteOrder.ps1        
-rw-r--r-- 1 christian 197121  231 Dec 10 10:44 DeletePet.ps1
-rw-r--r-- 1 christian 197121  358 Dec 10 10:44 DeleteUser.ps1
-rw-r--r-- 1 christian 197121  432 Dec 10 10:44 GetFindPetsByStatus.ps1
-rw-r--r-- 1 christian 197121  504 Dec 10 10:44 GetFindPetsByTags.ps1  
-rw-r--r-- 1 christian 197121  371 Dec 10 10:44 GetInventory.ps1       
-rw-r--r-- 1 christian 197121  247 Dec 10 10:44 GetLoginUser.ps1       
-rw-r--r-- 1 christian 197121  291 Dec 10 10:44 GetLogoutUser.ps1      
-rw-r--r-- 1 christian 197121  540 Dec 10 10:44 GetOrderById.ps1
-rw-r--r-- 1 christian 197121  275 Dec 10 10:44 GetPetById.ps1
-rw-r--r-- 1 christian 197121  245 Dec 10 10:44 GetUserByName.ps1
-rw-r--r-- 1 christian 197121  513 Dec 10 10:44 PostAddPet.ps1
-rw-r--r-- 1 christian 197121  521 Dec 10 10:44 PostCreateUser.ps1
-rw-r--r-- 1 christian 197121  610 Dec 10 10:44 PostCreateUsersWithListInput.ps1
-rw-r--r-- 1 christian 197121  464 Dec 10 10:44 PostPlaceOrder.ps1
-rw-r--r-- 1 christian 197121  299 Dec 10 10:44 PostUpdatePetWithForm.ps1
-rw-r--r-- 1 christian 197121  274 Dec 10 10:44 PostUploadFile.ps1
-rw-r--r-- 1 christian 197121  513 Dec 10 10:44 PutUpdatePet.ps1
-rw-r--r-- 1 christian 197121  541 Dec 10 10:44 PutUpdateUser.ps1

I’m probably not going to use this tool myself that much since after my experiments with text-based editors, I quickly realized that this is probably not for me. cURL Request Generator has the same (well almost the same) feature set as HTTP File Generator, so naturally this also supports generator requests with Authorization headers. It’s important to note that the scripts generated by cURL Request Generator are not meant to be added to source control. They were designed like HTTP File Generator in the way that they replace the SwaggerUI flow, where the requests are reset every time you load SwaggerUI. These generated cURL requests in powershell scripts containing Authorization headers should NEVER be committed to source control

Replacing SwaggerUI

I spend all my working hours building software that runs on Microsoft Azure and I extensively use the Azure CLI for various purposes. One of which is for retrieving an access token for the user I’m currently signed in as. With Azure CLI, you can request an access token based on a scope. This works great if your API uses roles that are specified in Microsoft Entra ID.

Here’s an advanced example of generating cURL requests in powershell scripts for a REST API that uses the Microsoft Entra ID service as an STS.

For this example, I use PowerShell and Azure CLI to retrieve an access token for the user I’m currently logged in with then I pipe the access token to cURL File Generator.

az account get-access-token --scope [Some Application ID URI]/.default `
| ConvertFrom-Json `
| %{
    curlgenerator `
        https://api.example.com/swagger/v1/swagger.json `
        --authorization-header ("Bearer " + $_.accessToken) `
        --base-url https://api.example.com `
        --output ./HttpFiles 
}

This script is something that you can have in projects and configure git to ignore the folder containing the generated cURL script files, since I re-generated them multiple times a day and they always contain Authorization headers.

You can also use the --azure-scope and azure-tenant-id arguments internally use DefaultAzureCredentials from the Microsoft.Extensions.Azure NuGet package to retrieve an access token for the specified scope.

curlgenerator `
    https://api.example.com/swagger/v1/swagger.json `
    --azure-scope [Some Application ID URI]/.default `
    --base-url https://api.example.com `
    --output ./HttpFiles 

I hope you found this useful and get inspired to try new things or to start building tools like this of your own. cURL Request Generator is free and open-source so please check it out.