Transforming Any Command-Line App into a Remote API

Introduction
Not all software starts with an API, but any command-line tool can be turned into a remote-access API within days with the right approach. Whether it's an image processor, data analysis tool, or astronomical plate solver like in our example, wrapping command-line applications with an API allows for web-based automation, remote access, and integration with modern platforms.
This article walks through the process of turning a command-line application into an API, using Go and a simple HTTP server to expose command execution over the web.
Why Turn a CLI App into an API?
✅ Enable Remote Access – Run the application from anywhere, not just on a local machine.
✅ Scalability – Allow multiple users or systems to interact with the tool simultaneously.
✅ Automation & Integration – Integrate with other services, like web apps, AI pipelines, or cloud computing workflows.
✅ Modernization – Upgrade legacy tools without rewriting them from scratch.
Example: Exposing a Plate Solver via an API
The following Go-based implementation takes an existing command-line plate solver and turns it into an API that can be accessed remotely.
Step 1: Create an HTTP Server in Go
First, we set up a basic HTTP server that listens for requests and executes the command-line tool.
package main
import (
"fmt"
"log"
"net/http"
"os/exec"
)
// API handler to process an image with the CLI tool
func solveHandler(w http.ResponseWriter, r *http.Request) {
// Extract parameters from request
imageFile := r.URL.Query().Get("image")
if imageFile == "" {
http.Error(w, "Missing image parameter", http.StatusBadRequest)
return
}
// Construct the CLI command
cmd := exec.Command("./platesolver", "-f", imageFile)
output, err := cmd.CombinedOutput()
if err != nil {
http.Error(w, fmt.Sprintf("Error executing solver: %s", err), http.StatusInternalServerError)
return
}
// Return solver output
fmt.Fprintf(w, "%s", output)
}
func main() {
http.HandleFunc("/solve", solveHandler)
fmt.Println("Server is running on port 8080...")
log.Fatal(http.ListenAndServe(":8080", nil))
}
Step 2: Running the API Server
Compile and run the Go server:
go build -o api_server main.go
./api_server
Step 3: Sending Requests to the API
Once the server is running, send a request to process an image with the CLI tool:
curl "http://localhost:8080/solve?image=test.fits"
The server will execute ./platesolver -f test.fits
and return the output.
Expanding Functionality
Handling File Uploads
Instead of passing filenames, the API can accept file uploads and process them before execution.
func uploadHandler(w http.ResponseWriter, r *http.Request) {
file, _, err := r.FormFile("image")
if err != nil {
http.Error(w, "Failed to read file", http.StatusBadRequest)
return
}
defer file.Close()
tempFile, err := os.CreateTemp("./uploads", "upload-*.fits")
if err != nil {
http.Error(w, "Failed to save file", http.StatusInternalServerError)
return
}
defer tempFile.Close()
io.Copy(tempFile, file)
fmt.Fprintf(w, "File uploaded: %s", tempFile.Name())
}
Adding Authentication
To secure the API, API keys or JWT tokens can be used to restrict access.
func authMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
apiKey := r.Header.Get("X-API-KEY")
if apiKey != "your-secret-key" {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
next.ServeHTTP(w, r)
})
}
Conclusion
By wrapping command-line tools with a lightweight API, legacy applications can be modernized and accessed remotely without modifying their core functionality. This approach enables scalability, automation, and cloud integration while keeping development time minimal.
🚀 Looking to convert your CLI tool into a web API? AKADATA can help you build scalable solutions quickly!