Skip to content

Latest commit

 

History

History
625 lines (517 loc) · 16.6 KB

File metadata and controls

625 lines (517 loc) · 16.6 KB

Vayu Engine API Reference

Base URL: http://127.0.0.1:9876 (default, configurable via --port)

All endpoints return JSON. Error responses follow this format:

{
  "error": "Error message"
}

Health & Configuration

GET /health

Check engine status and version.

Response:

{
  "status": "ok",
  "version": "0.3.0",
  "workers": 8
}

GET /config

Get global configuration settings. Backed by the config_entries table; each entry carries UI metadata (label, description, category, default, min/max). Keys include:

{
  "workers": 8,
  "maxConnections": 10000,
  "defaultTimeout": 30000,
  "statsInterval": 100,
  "contextPoolSize": 64,
  "liveTickIntervalMs": 100,
  "liveRetentionMs": 60000
}

POST /config validates each key against its registered type and min/max range.

Collections

Collections are folders that organize requests in a hierarchy.

GET /collections

List all collections.

Response:

[
  {
    "id": "col_1234567890",
    "name": "My API",
    "parentId": null,
    "variables": {},
    "order": 0,
    "createdAt": 1234567890
  }
]

POST /collections

Create or update a collection. If id is provided and exists, performs an update.

Request:

{
  "id": "col_1234567890",  // Optional, auto-generated if omitted
  "name": "My API",
  "parentId": null,         // Optional, null for root
  "order": 0,               // Optional
  "variables": {}           // Optional, collection-scoped variables
}

Response: The saved collection object.

DELETE /collections/:id

Delete a collection and all its requests (cascading delete).

Response:

{
  "message": "Collection deleted successfully",
  "id": "col_1234567890"
}

Requests

GET /requests

List requests in a collection.

Query Parameters:

  • collectionId (required): Collection ID to fetch requests from

Response:

[
  {
    "id": "req_1234567890",
    "collectionId": "col_1234567890",
    "name": "Get Users",
    "method": "GET",
    "url": "{{baseUrl}}/users",
    "params": {},
    "headers": {},
    "body": "",
    "bodyType": "none",
    "auth": {},
    "preRequestScript": "",
    "postRequestScript": "",
    "updatedAt": 1234567890
  }
]

POST /requests

Create or update a request. If id is provided and exists, performs an update.

Request:

{
  "id": "req_1234567890",           // Optional, auto-generated if omitted
  "collectionId": "col_1234567890", // Required for new requests
  "name": "Get Users",               // Required for new requests
  "method": "GET",                   // Required for new requests: GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS
  "url": "{{baseUrl}}/users",        // Required for new requests
  "params": {},                      // Optional, query parameters
  "headers": {},                     // Optional, request headers
  "body": "",                        // Optional, request body
  "bodyType": "none",                // Optional: "none", "json", "text", "form-data", "x-www-form-urlencoded"
  "auth": {},                        // Optional, authentication config
  "preRequestScript": "",            // Optional, JavaScript pre-request script
  "postRequestScript": ""            // Optional, JavaScript test script
}

Response: The saved request object.

DELETE /requests/:id

Delete a request.

Response:

{
  "message": "Request deleted successfully",
  "id": "req_1234567890"
}

Environments

GET /environments

List all environments.

Response:

[
  {
    "id": "env_1234567890",
    "name": "Production",
    "variables": {
      "baseUrl": {
        "value": "https://api.example.com",
        "enabled": true,
        "secret": false
      }
    },
    "updatedAt": 1234567890
  }
]

POST /environments

Create or update an environment.

Request:

{
  "id": "env_1234567890",  // Optional, auto-generated if omitted
  "name": "Production",    // Required for new environments
  "variables": {            // Optional
    "baseUrl": {
      "value": "https://api.example.com",
      "enabled": true,
      "secret": false
    }
  }
}

Response: The saved environment object.

DELETE /environments/:id

Delete an environment.

Response:

{
  "message": "Environment deleted successfully",
  "id": "env_1234567890"
}

Global Variables

GET /globals

Get global variables (singleton).

Response:

{
  "id": "globals",
  "variables": {
    "apiKey": {
      "value": "xxx",
      "enabled": true,
      "secret": false
    }
  },
  "updatedAt": 1234567890
}

POST /globals

Set global variables.

Request:

{
  "variables": {
    "apiKey": {
      "value": "xxx",
      "enabled": true,
      "secret": false
    }
  }
}

Response: The saved globals object.

Execution

POST /request

Execute a single HTTP request (Design Mode). Returns immediate response with test results.

Request:

{
  "method": "POST",
  "url": "https://api.example.com/users",
  "headers": {
    "Content-Type": "application/json"
  },
  "body": {
    "type": "json",
    "content": "{\"name\":\"John\"}"
  },
  "requestId": "req_1234567890",      // Optional, links to saved request
  "environmentId": "env_1234567890",  // Optional, uses environment variables
  "preRequestScript": "",              // Optional
  "postRequestScript": "pm.test('Status is 200', () => pm.expect(pm.response.code).to.equal(200));"
}

Response:

{
  "runId": "run_1234567890",
  "statusCode": 200,
  "statusText": "OK",
  "headers": {
    "content-type": "application/json"
  },
  "body": "{\"id\":1,\"name\":\"John\"}",
  "bodySize": 20,
  "timing": {
    "totalMs": 245.5,
    "wireMs": 245.1,
    "queueWaitMs": 0.4,
    "dnsMs": 5.2,
    "connectMs": 12.3,
    "tlsMs": 45.1,
    "firstByteMs": 180.2,
    "downloadMs": 2.7
  },
  "testResults": [
    {
      "name": "Status is 200",
      "passed": true
    }
  ],
  "consoleLogs": []
}

POST /run

Start a load test run (Vayu Mode).

Request:

{
  "method": "GET",
  "url": "https://api.example.com/users",
  "headers": {},
  "body": {
    "type": "none",
    "content": ""
  },
  "mode": "constant_rps",    // "constant_rps", "constant_concurrency", "ramp_up", or "iterations"
  "concurrency": 100,        // Target in-flight requests (constant_concurrency / ramp_up target / iterations)
  "startConcurrency": 1,     // Ramp start concurrency (ramp_up mode)
  "duration": "60s",         // Duration (constant_rps / constant_concurrency / ramp_up)
  "rampUpDuration": "10s",   // Ramp-up time (ramp_up mode)
  "iterations": 0,           // Number of iterations (iterations mode)
  "targetRps": 1000,         // Target requests per second (constant_rps mode)
  "maxInFlight": 10000,      // Optional; see "maxInFlight" note below — constant_rps only
  "requestId": "req_1234567890",      // Optional, links to saved request
  "environmentId": "env_1234567890",  // Optional
  "tests": ""                // Optional, deferred validation script
}

Response:

{
  "runId": "run_1234567890",
  "status": "running"
}

Concurrency model. constant_concurrency, ramp_up, and iterations are closed-loop: the engine holds in-flight requests at a target (concurrency, or the ramp curve from startConcurrency to concurrency) — when a request completes, another is issued. Throughput is a result (concurrency ÷ latency), not an input. constant_rps is open-loop: it dispatches at targetRps regardless of how fast responses return.

maxInFlight. A hard cap on concurrent in-flight requests. It applies only to constant_rps (the open-loop rate mode), where it bounds how many requests may be outstanding before the engine drops or queues new ones; default ≈ max(targetRps × 10, 1000). For the closed-loop modes the concurrency target is the in-flight bound, so maxInFlight is ignored there.

Metrics & Statistics

GET /stats/:runId

Prefer GET /metrics/live/:runId (below) for live dashboards — it replays a retained in-memory tick topic with no attach race. /stats/:runId is the legacy DB-polling path; it remains useful for historical retrieval via ?format=json&limit=&offset= (paginated time-series read), which the app uses to hydrate the history view.

Stream real-time metrics for a load test using Server-Sent Events (SSE).

Response: SSE stream with events:

event: stats
data: {"timestamp":1234567890,"totalRequests":1500,"totalErrors":5,"totalSuccess":1495,"errorRate":0.33,"avgLatencyMs":45.2,"currentRps":150.5,"activeConnections":100,"elapsedSeconds":10.5}

event: complete
data: {"totalRequests":6000,"totalErrors":30,"totalSuccess":5970,"errorRate":0.5,"avgLatencyMs":42.1,"finalRps":100.0,"duration":60.0}

Metrics included:

  • totalRequests: Total requests completed
  • totalErrors: Total errors encountered
  • totalSuccess: Total successful requests
  • errorRate: Error rate as percentage
  • avgLatencyMs: Average latency in milliseconds
  • currentRps: Current requests per second
  • activeConnections: Active concurrent connections
  • elapsedSeconds: Elapsed time since test start

GET /metrics/live/:runId

Stream live metrics for a run via Server-Sent Events, replayed from a retained in-memory tick topic. The engine produces one wire-ready metrics tick per liveTickIntervalMs (default 100ms) into a per-run buffer; this endpoint replays that buffer from offset 0 and then tails new ticks until the run finishes, ending with a complete event. Because the topic is retained for liveRetentionMs (default 60000ms) after completion, a client that connects late — even after a sub-second run has already finished — still receives the full series. There is no attach race.

Events:

event: metrics
id: 0
data: {"runId":"...","timestamp":1234567890,"elapsedSeconds":10.5,
       "totalRequests":1500,"totalSuccess":1495,"totalErrors":5,"errorRate":0.33,
       "currentRps":150.5,"sendRate":150.0,"throughput":149.5,
       "activeConnections":100,"backpressure":0,"droppedRequests":0,
       "avgLatencyMs":45.2,"avgQueueWaitMs":0.4,
       "latencyP50Ms":38.5,"latencyP95Ms":95.1,"latencyP99Ms":156.7,
       "bytesSent":48000,"bytesReceived":1920000,
       "requestsSent":1500,"requestsExpected":0,
       "status2xx":1495,"status3xx":0,"status4xx":3,"status5xx":2,
       "statusCodes":{"200":1495,"404":3,"500":2}}

event: complete
data: {"event":"complete","runId":"run_1234567890"}

Field reference (all keys emitted by MetricsCollector::get_current_stats()):

Field Meaning
totalRequests / totalSuccess / totalErrors Completed counts
errorRate Error percentage
currentRps Instantaneous RPS (delta over the tick window)
sendRate Rate requests are dispatched (open model)
throughput Rate responses are received
activeConnections Current in-flight requests
backpressure Queue depth (requestsSent − totalRequests)
droppedRequests Requests discarded at the maxInFlight cap (never sent)
avgLatencyMs Mean perceived latency
avgQueueWaitMs Mean time queued inside the generator before the wire
latencyP50Ms / latencyP95Ms / latencyP99Ms Live percentiles (cumulative HdrHistogram)
bytesSent / bytesReceived Cumulative wire bytes
requestsSent / requestsExpected Progress for bounded modes (drives ETA)
status2xxstatus5xx Per-class counts
statusCodes Full per-code distribution map

Each metrics event carries an id: equal to its zero-based offset. The browser's built-in EventSource retry automatically replays this id as Last-Event-ID on its own intra-connection retries (no application code needed), and the stream resumes from Last-Event-ID + 1.

Application-level reconnect: clients that close the EventSource themselves (e.g. after observing readyState === CLOSED) should NOT open a new connection and rely on Last-Event-IDEventSource does not expose a header-setting API, so a fresh connect would request from=0 and replay the entire retained topic, duplicating ticks already shown. The canonical recovery is to converge on the stored report via GET /run/:runId/report (the same path used at normal run end). This is the pattern the bundled app uses.

Responses:

  • 200 — SSE stream (active run, or finished run still within the retention window).
  • 404 — run not found or evicted past liveRetentionMs; the body hints Use /run/:runId/report for the stored report. Clients should fall back to the stored report in this case.

Tuning: liveTickIntervalMs (live tick cadence, 10–1000ms) and liveRetentionMs (post-completion retention, 0–600000ms; 0 disables retention) are configurable via POST /config.

Runs

GET /runs

List all test runs (both design mode and load tests).

Response:

[
  {
    "id": "run_1234567890",
    "requestId": "req_1234567890",
    "environmentId": "env_1234567890",
    "type": "design",
    "status": "completed",
    "configSnapshot": "{}",
    "startTime": 1234567890,
    "endTime": 1234567891
  }
]

GET /run/:runId

Get details for a specific run.

Response: Run object with full details.

POST /run/:runId/stop

Stop a running load test.

Response:

{
  "message": "Run stopped",
  "runId": "run_1234567890"
}

GET /run/:runId/report

Get the final report for a completed run. Reconstructed from the runs, metrics, and results tables (there is no stored summary blob). The response is a nested object; conditional sections appear only when relevant (e.g. rateControl only for constant_rps, testValidation only when a test script ran).

Response:

{
  "metadata": {
    "runId": "run_1234567890",
    "runType": "load",
    "status": "completed",
    "startTime": 1234567890,
    "endTime": 1234567950,
    "requestUrl": "https://api.example.com/users",
    "requestMethod": "GET",
    "configuration": { "...": "config snapshot" }
  },
  "summary": {
    "totalRequests": 6000,
    "successfulRequests": 5970,
    "failedRequests": 30,
    "errorRate": 0.5,
    "totalDurationSeconds": 60.0,
    "avgRps": 100.0,
    "testDuration": 60.0,
    "sendRate": 100.0,
    "throughput": 99.5,
    "setupOverhead": 0.12,
    "peakConcurrency": 100,
    "droppedRequests": 0,
    "avgQueueWaitMs": 0.4,
    "bytesSent": 192000,
    "bytesReceived": 7680000,
    "throughputBytesPerSec": 128000
  },
  "latency": {
    "min": 12.3, "max": 1250.5, "avg": 42.1, "median": 38.5,
    "p50": 38.5, "p75": 45.2, "p90": 78.3, "p95": 95.1, "p99": 156.7, "p999": 450.2
  },
  "statusCodes": { "200": 5970, "500": 30 },
  "rateControl": { "targetRps": 100, "actualRps": 99.5, "achievement": 99.5 },
  "errors": {
    "total": 30,
    "withDetails": 30,
    "types": { "timeout": 20, "connection_failed": 10 },
    "byStatusCode": { "500": 30 }
  },
  "timingBreakdown": {
    "avgDnsMs": 5.2, "avgConnectMs": 12.3, "avgTlsMs": 45.1,
    "avgFirstByteMs": 180.2, "avgDownloadMs": 2.7
  },
  "slowRequests": { "count": 12, "thresholdMs": 1000, "percentage": 0.2 },
  "testValidation": { "samplesTested": 500, "testsPassed": 498, "testsFailed": 2, "successRate": 99.6 },
  "results": [ { "...": "sampled request/response outcomes" } ]
}

latency.* and the enriched summary fields (peakConcurrency, droppedRequests, avgQueueWaitMs, bytesSent/Received, throughputBytesPerSec) come from the persisted per-tick metrics rows. latency_ms in results (and therefore these percentiles) is perceived latency.

DELETE /run/:runId

Delete a run and all associated metrics/results.

Response:

{
  "message": "Run deleted successfully",
  "runId": "run_1234567890"
}

Scripting

GET /scripting/completions

Get script engine API completions for UI autocomplete.

Response:

{
  "version": "1.0.0",
  "engine": "quickjs",
  "completions": [
    {
      "label": "pm.test",
      "kind": 1,
      "insertText": "pm.test(\"${1:test name}\", function() {\n\t${2:// assertions}\n});",
      "detail": "pm.test(name: string, fn: () => void)",
      "documentation": "Define a test with assertions..."
    }
  ]
}

HTTP Status Codes

Code Meaning
200 Success
400 Bad request (invalid JSON, missing required fields)
404 Resource not found
500 Internal server error

Notes

  • All timestamps are Unix milliseconds (since epoch)
  • Variable substitution uses {{variableName}} syntax
  • Environment variables are resolved in order: environment → collection → global
  • Load test metrics are collected every 100ms
  • SSE connections timeout after 30 seconds of inactivity