Build Your Own Postman-Like API Testing Tool Using PHP

Build Your Own Postman-Like API Testing Tool Using PHP

In the world of modern web development, APIs serve as the backbone, connecting different systems and enabling seamless communication. While tools like Postman are popular for API testing and debugging, building your own custom API testing tool can provide a deeper understanding of HTTP protocols, APIs, and PHP’s capabilities.

In this article, we explore how you can create a Postman-like API testing tool using PHP. This tool offers functionalities to send requests, customize headers, pass parameters, and analyze responses—all from a clean and intuitive interface.

Follow this video for complete guidance:

Full Source Code – Postman Like API Testing Tool

<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $requestData = json_decode(file_get_contents('php://input'), true);
    $ch = curl_init();
    
    $url = $requestData['url'];
    if (!empty($requestData['params'])) {
        $queryString = http_build_query($requestData['params']);
        $url .= (strpos($url, '?') !== false ? '&' : '?') . $queryString;
    }
    
    $headers = [];
    if (!empty($requestData['headers'])) {
        foreach ($requestData['headers'] as $key => $value) {
            $headers[] = "$key: $value";
        }
    }
    
    curl_setopt_array($ch, [
        CURLOPT_URL => $url,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_CUSTOMREQUEST => $requestData['method'],
        CURLOPT_FOLLOWLOCATION => true,
        CURLOPT_TIMEOUT => 30,
        CURLOPT_HTTPHEADER => $headers
    ]);

    
    if (in_array($requestData['method'], ['POST', 'PUT', 'PATCH'])) {
        curl_setopt($ch, CURLOPT_POSTFIELDS, $requestData['body']);
    }
    
    $startTime = microtime(true);
    $response = curl_exec($ch);
    $duration = round((microtime(true) - $startTime) * 1000);
    
    $result = [
        'status' => curl_getinfo($ch, CURLINFO_HTTP_CODE),
        'contentType' => curl_getinfo($ch, CURLINFO_CONTENT_TYPE),
        'duration' => $duration,
        'response' => $response,
        'error' => curl_error($ch)
    ];
    
    curl_close($ch);
    header('Content-Type: application/json');
    echo json_encode($result);
    exit;
}
?>
<!DOCTYPE html>
<html lang="en" data-bs-theme="dark">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>API Testing Tool</title>
    <link href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.2/css/bootstrap.min.css" rel="stylesheet">
    <style>
        :root {
            --dark-bg: #1a1a1a;
            --darker-bg: #141414;
            --border-color: #2d2d2d;
        }
        
        body {
            background-color: var(--dark-bg);
            color: #e0e0e0;
        }
        
        .sidebar {
            background-color: var(--darker-bg);
            border-right: 1px solid var(--border-color);
        }
        
        .form-control, .form-select {
            background-color: var(--darker-bg);
            border-color: var(--border-color);
            color: #e0e0e0;
        }
        
        .form-control:focus, .form-select:focus {
            background-color: var(--darker-bg);
            border-color: #0d6efd;
            color: #e0e0e0;
        }
        
        .textarea-container {
            height: calc(50vh - 200px);
        }
        
        textarea {
            font-family: monospace !important;
            resize: none;
        }
        
        .nav-tabs {
            border-bottom: 1px solid var(--border-color);
        }
        
        .nav-tabs .nav-link.active {
            background-color: var(--dark-bg);
            border-color: var(--border-color) var(--border-color) var(--dark-bg);
            color: #fff;
        }
        
        .nav-tabs .nav-link:not(.active) {
            color: #888;
        }
        
        .card {
            background-color: var(--darker-bg);
            border: 1px solid var(--border-color);
        }
        
        .response-section {
            background-color: var(--darker-bg) !important;
            border: 1px solid var(--border-color) !important;
        }
    </style>
</head>
<body>
    <div class="container-fluid">
        <div class="row vh-100">
            <!-- Sidebar -->
            <div class="col-md-3 col-lg-2 sidebar p-3">
                <h4 class="text-light">History</h4>
                <div id="history" class="overflow-auto" style="max-height: 90vh;"></div>
            </div>

            <!-- Main Content -->
            <div class="col-md-9 col-lg-10 p-3">
                <!-- URL Bar -->
                <div class="row mb-3">
                    <div class="col-auto">
                        <select id="method" class="form-select">
                            <option>GET</option>
                            <option>POST</option>
                            <option>PUT</option>
                            <option>DELETE</option>
                            <option>PATCH</option>
                        </select>
                    </div>
                    <div class="col">
                        <input type="text" id="url" class="form-control" placeholder="Enter request URL">
                    </div>
                    <div class="col-auto">
                        <button id="sendBtn" class="btn btn-primary">Send</button>
                    </div>
                </div>

                <!-- Request Tabs -->
                <ul class="nav nav-tabs mb-3">
                    <li class="nav-item">
                        <a class="nav-link active" data-bs-toggle="tab" href="#params">Params</a>
                    </li>
                    <li class="nav-item">
                        <a class="nav-link" data-bs-toggle="tab" href="#headers">Headers</a>
                    </li>
                    <li class="nav-item">
                        <a class="nav-link" data-bs-toggle="tab" href="#body">Body</a>
                    </li>
                </ul>

                <!-- Tab Content -->
                <div class="tab-content mb-3">
                    <div class="tab-pane fade show active textarea-container" id="params">
                        <textarea id="paramsInput" class="form-control" placeholder="Enter query parameters as JSON">{}</textarea>
                    </div>
                    <div class="tab-pane fade textarea-container" id="headers">
                        <textarea id="headersInput" class="form-control" placeholder="Enter headers as JSON">{}</textarea>
                    </div>
                    <div class="tab-pane fade textarea-container" id="body">
                        <textarea id="bodyInput" class="form-control" placeholder="Enter request body"></textarea>
                    </div>
                </div>

                <!-- Response Section -->
                <div class="p-3 response-section rounded">
                    <div class="d-flex justify-content-between align-items-center mb-2">
                        <span id="status"></span>
                        <span id="contentType" class="text-muted"></span>
                        <span id="time" class="text-muted"></span>
                    </div>
                    <textarea id="responseOutput" class="form-control" readonly></textarea>
                </div>
            </div>
        </div>
    </div>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.2/js/bootstrap.bundle.min.js"></script>
    <script>
        $(document).ready(function() {
            const history = [];

            $('#sendBtn').click(async function() {
                try {
                    const result = await $.ajax({
                        url: '',
                        method: 'POST',
                        contentType: 'application/json',
                        data: JSON.stringify({
                            method: $('#method').val(),
                            url: $('#url').val(),
                            params: JSON.parse($('#paramsInput').val() || '{}'),
                            headers: JSON.parse($('#headersInput').val() || '{}'),
                            body: $('#bodyInput').val()
                        })
                    });

                    const statusClass = result.status < 400 ? 'success' : 'danger';
                    $('#status').html(`<span class="badge bg-${statusClass}">Status: ${result.status}</span>`);
                    $('#contentType').text(result.contentType);
                    $('#time').text(`${result.duration}ms`);
                    $('#responseOutput').val(formatJSON(result.response));

                    history.unshift({
                        method: $('#method').val(),
                        url: $('#url').val(),
                        status: result.status
                    });
                    updateHistory();

                } catch (error) {
                    $('#status').html('<span class="badge bg-danger">Error</span>');
                    $('#responseOutput').val(error.message);
                }
                autoResize(document.getElementById('responseOutput'));
            });

            function formatJSON(str) {
                try {
                    return JSON.stringify(JSON.parse(str), null, 2);
                } catch {
                    return str;
                }
            }

            function updateHistory() {
                const historyHtml = history.map(item => `
                    <div class="card mb-2" style="cursor: pointer;">
                        <div class="card-body p-2" data-method="${item.method}" data-url="${item.url}">
                            <span class="badge bg-${item.status < 400 ? 'success' : 'danger'}">${item.status}</span>
                            <strong>${item.method}</strong>
                            <div class="text-truncate">${item.url}</div>
                        </div>
                    </div>
                `).join('');
                
                $('#history').html(historyHtml);
            }

            function autoResize(textarea) {
                textarea.style.height = 'auto';
                textarea.style.height = textarea.scrollHeight + 'px';
            }

            $('#responseOutput').on('input change', function() {
                autoResize(this);
            });
            $('#paramsInput').on('input change', function() {
                autoResize(this);
            });
            $('#headersInput').on('input change', function() {
                autoResize(this);
            });
            $('#bodyInput').on('input change', function() {
                autoResize(this);
            });

            $(document).on('click', '.card-body', function() {
                $('#method').val($(this).data('method'));
                $('#url').val($(this).data('url'));
            });
        });
    </script>
</body>
</html>

Why Build Your Own API Testing Tool?

  • Learning Opportunity: Developing your own tool allows you to understand how HTTP requests and responses work. You’ll dive deep into concepts like headers, query parameters, and response codes.
  • Customization: Unlike ready-made tools, your custom-built tool can be tailored to your specific needs, such as integrating with other systems or adding unique features.
  • Portability: Hosting this tool on a web server makes it accessible from anywhere, without the need for local installations or software.
  • Cost-Effectiveness: For developers or organizations looking for a budget-friendly solution, creating a tool in-house can save costs.

Key Features of the Tool

Here are the core features included in the API testing tool you can build:

  • Multiple Request Methods: Support for GET, POST, PUT, DELETE, and PATCH methods to cover the most common API actions.
  • Customizable Headers: Ability to set headers like Content-Type or authentication tokens.
  • Query Parameters: Add key-value pairs for dynamic API requests.
  • Request Body: Input JSON data for POST, PUT, or PATCH requests.
  • Response Display: View the status code, content type, response time, and raw response body.
  • Request History: Save and view previously made requests for quick re-testing.

The Backend Workflow

The backend of the tool, powered by PHP, handles incoming data, makes HTTP requests using cURL, and returns the API responses. Here’s a simplified explanation of the backend’s workflow:

  • Request Data Handling: PHP accepts a JSON payload containing the URL, HTTP method, headers, query parameters, and body.
  • Dynamic URL Construction: Query parameters are appended to the URL dynamically, ensuring compatibility with APIs.
  • Header Configuration: Custom headers are added, defaulting to Content-Type: application/json if none are provided.
  • Sending Requests: The cURL library is used to send requests, supporting timeouts and redirects for robust handling.
  • Response Metrics: PHP captures the response, HTTP status code, content type, and execution time.
  • Error Handling: If the API call fails, error messages are captured and displayed.

Front-End Interface

The tool features a sleek and responsive user interface built with HTML, CSS, and Bootstrap. The interface offers:

  • URL Input Bar: Enter the API endpoint URL along with the HTTP method.
  • Request Tabs: Separate tabs for query parameters, headers, and body input make the tool intuitive.
  • Response Viewer: Display the HTTP status, content type, response time, and formatted JSON response.
  • History Sidebar: Quickly access previously tested API calls with details like method, URL, and status.

What You’ll Learn

Building this API testing tool will teach you:

  • How to use PHP’s cURL library for HTTP requests.
  • Handling JSON data in PHP for input and output.
  • Optimizing front-end UI with Bootstrap and JavaScript.
  • Managing request history and dynamically displaying it in a sidebar.
  • Debugging common API-related issues, such as handling timeouts or invalid responses.

Extending the Tool

Once you’ve built the basic version, you can add advanced features to enhance its functionality:

  • Authentication Support: Add OAuth or API key support for testing protected endpoints.
  • File Uploads: Enable file attachments for testing upload APIs.
  • Environment Variables: Save base URLs or common headers as environment variables.
  • Export and Import: Allow users to export and import API request collections.
  • Code Snippet Generator: Generate cURL or language-specific code snippets for API calls.

Creating your own Postman-like API testing tool in PHP is not just a rewarding project but also a powerful way to understand the intricacies of APIs. Whether you’re a beginner eager to expand your programming skills or a seasoned developer looking for a customized solution, this project is a step in the right direction.

By the end of this journey, you’ll have a lightweight, web-based API testing tool that you can use, modify, and improve over time. So why wait? Roll up your sleeves and start building today!

Related Posts

Leave a Reply

Your email address will not be published. Required fields are marked *