Skip to content

Aditya-tec/api-testing-platform

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

15 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Distributed API Workflow Execution Platform

A full-stack platform for defining, running, and monitoring API test collectionsβ€”built to replace ad-hoc Postman runs with repeatable execution, queued workers, results history, and metrics.


Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                        CLIENT / BROWSER                             β”‚
β”‚                                                                     β”‚
β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚   β”‚                  Next.js Dashboard                          β”‚   β”‚
β”‚   β”‚                                                             β”‚   β”‚
β”‚   β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚   β”‚
β”‚   β”‚   β”‚ Collections β”‚  β”‚  Job Detail β”‚  β”‚     Metrics      β”‚  β”‚   β”‚
β”‚   β”‚   β”‚    Page     β”‚  β”‚  + Live Log β”‚  β”‚    Dashboard     β”‚  β”‚   β”‚
β”‚   β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚   β”‚
β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”‚β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                 β”‚ HTTP REST
                                 β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                        EXPRESS API SERVER                           β”‚
β”‚                                                                     β”‚
β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚   β”‚  Collections β”‚  β”‚     Jobs     β”‚  β”‚  Metrics  β”‚  β”‚  Auth   β”‚  β”‚
β”‚   β”‚   /api/v1/   β”‚  β”‚   /api/v1/   β”‚  β”‚ /api/v1/  β”‚  β”‚Profiles β”‚  β”‚
β”‚   β”‚  collections β”‚  β”‚    jobs      β”‚  β”‚  metrics  β”‚  β”‚         β”‚  β”‚
β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”‚                             β”‚                                       β”‚
β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚   β”‚                    Middleware Layer                         β”‚   β”‚
β”‚   β”‚   Helmet Β· CORS Β· Rate Limiter Β· Input Validator Β· SSRF    β”‚   β”‚
β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”‚β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                              β”‚ Enqueue job ID
                              β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                        REDIS (Upstash)                              β”‚
β”‚                                                                     β”‚
β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚   β”‚                     BullMQ Queue                            β”‚   β”‚
β”‚   β”‚              job-execution  ──────►  [jobId, jobId, ...]   β”‚   β”‚
β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”‚β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                  β”‚ Consume
                                  β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                           WORKER PROCESS                            β”‚
β”‚                                                                     β”‚
β”‚   1. Fetch Job snapshot from MongoDB                                β”‚
β”‚   2. For each request in snapshot:                                  β”‚
β”‚                                                                     β”‚
β”‚      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚      β”‚  SSRF Check β†’ Inject Variables β†’ Fire HTTP (axios)       β”‚   β”‚
β”‚      β”‚       ↓                ↓                  ↓              β”‚   β”‚
β”‚      β”‚  Block if          {{variable}}       Retry with         β”‚   β”‚
β”‚      β”‚  internal IP       replaced from      exponential        β”‚   β”‚
β”‚      β”‚  detected          prior response     backoff            β”‚   β”‚
β”‚      β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β”‚                                                                     β”‚
β”‚   3. Write RequestResult + Log events back to MongoDB               β”‚
β”‚   4. Update Job status: PENDING β†’ RUNNING β†’ COMPLETED/PARTIAL/FAILEDβ”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                  β”‚
                                  β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                       MONGODB ATLAS                                 β”‚
β”‚                                                                     β”‚
β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”            β”‚
β”‚   β”‚  Collection  β”‚  β”‚     Job      β”‚  β”‚ RequestResult β”‚            β”‚
β”‚   β”‚              β”‚  β”‚              β”‚  β”‚               β”‚            β”‚
β”‚   β”‚ name         β”‚  β”‚ collectionId β”‚  β”‚ jobId         β”‚            β”‚
β”‚   β”‚ requests[]   β”‚  β”‚ snapshot[]   β”‚  β”‚ requestIndex  β”‚            β”‚
β”‚   β”‚ executionModeβ”‚  β”‚ authSnapshot β”‚  β”‚ httpStatus    β”‚            β”‚
β”‚   β”‚ authProfileIdβ”‚  β”‚ variables{}  β”‚  β”‚ latencyMs     β”‚            β”‚
β”‚   β”‚              β”‚  β”‚ status       β”‚  β”‚ retryCount    β”‚            β”‚
β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜            β”‚
β”‚                                                                     β”‚
β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚   β”‚  AuthProfile β”‚  β”‚                  Log                     β”‚   β”‚
β”‚   β”‚              β”‚  β”‚                                          β”‚   β”‚
β”‚   β”‚ name         β”‚  β”‚ event: JOB_STARTED                       β”‚   β”‚
β”‚   β”‚ type         β”‚  β”‚      | REQUEST_STARTED                   β”‚   β”‚
β”‚   β”‚ token (AES   β”‚  β”‚      | VAR_EXTRACTED                     β”‚   β”‚
β”‚   β”‚  encrypted)  β”‚  β”‚      | REQUEST_RETRYING                  β”‚   β”‚
β”‚   β”‚ headerName   β”‚  β”‚      | REQUEST_COMPLETED                 β”‚   β”‚
β”‚   β”‚              β”‚  β”‚      | REQUEST_SKIPPED                   β”‚   β”‚
β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚      | JOB_COMPLETED / PARTIAL / FAILED  β”‚   β”‚
β”‚                     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

What this app is for (use cases)

  • Regression checks for APIs: run the same collection repeatedly and compare outcomes over time.
  • Smoke tests after deployments: trigger a job run and review pass/fail by request.
  • Multi-step API workflows: extract values from one response (e.g., userId, token) and inject them into later requests.
  • Team visibility & auditability: keep a persistent execution history with per-request results and an event trail.
  • Performance awareness: track latency trends and identify slow requests across many runs.

Core concepts

  • Collection: a saved definition of API requests (method, URL, headers, body, timeout) with an execution mode (sequential or parallel).
  • Job: one execution of a collection. Jobs store a snapshot of the collection at run time so reruns stay deterministic even if the collection later changes.
  • Request Result: one record per request within a job (status, HTTP status, latency, response snippet, retries, error message).
  • Event Log: an append-only timeline for job execution (job/request lifecycle, retries, variable extraction events).
  • Auth Profile: reusable credential storage (bearer token or API key) that can be linked to collections; secrets are encrypted at rest and never returned by the API.

Key features

  • Queued execution with workers: jobs are enqueued and processed by a separate worker process (with configurable concurrency via WORKER_CONCURRENCY env var).
  • Deterministic reruns: rerun a job using the original request/auth snapshots via POST /jobs/:id/rerun.
  • Sequential "workflow" mode: supports {{variable}} placeholders and response-to-variable extraction (dot-path rules) so later requests can depend on earlier outputs.
  • SKIP propagation: if a request fails in sequential mode, all subsequent requests are marked SKIPPED β€” not silently ignored.
  • PARTIAL status: jobs can finish as COMPLETED, FAILED, or PARTIAL β€” for when some requests pass and others fail.
  • Retries with exponential backoff: request execution retries transient failures and records REQUEST_RETRYING log events with attempt number and reason.
  • Safety controls for outbound requests: SSRF protection blocks private/internal network targets and unsafe protocols (including DNS-rebinding protection via pre-flight DNS resolution).
  • Credential security: auth profile tokens are AES-256 encrypted in MongoDB; authSnapshot.token is always returned as [REDACTED] in API responses.
  • Observability endpoints: global metrics and per-collection metrics (avg/min/max latency per request across all runs) via MongoDB aggregation pipelines.
  • OpenAPI documentation: Swagger UI available at /api/v1/docs.
  • CI/CD pipeline: GitHub Actions runs 27 tests on every push β€” deploy is blocked if any fail.

System architecture (high level)

  • Frontend dashboard (frontend/)

    • Create and manage collections
    • Trigger runs and review job detail (results + logs)
    • View system metrics and per-collection performance
  • Backend API (src/)

    • REST resources for collections, jobs, metrics, and auth profiles
    • Persists definitions and execution history in MongoDB
    • Enqueues jobs to a Redis-backed queue
  • Worker (src/jobs/worker.js)

    • Consumes queued jobs
    • Executes request snapshots (sequential orchestration, retries, variable extraction)
    • Writes request results + log events back to MongoDB

Tech stack

Frontend

  • Next.js (App Router)
  • React
  • Tailwind CSS

Backend

  • Node.js + Express
  • MongoDB + Mongoose
  • BullMQ (job queue) + Redis (queue backend)
  • Axios (outbound request execution)
  • Swagger / OpenAPI via swagger-jsdoc + swagger-ui-express

Security & reliability

  • Helmet (security headers)
  • CORS with environment-based origin control
  • Rate limiting for API routes and job creation
  • SSRF protection with DNS rebinding defence
  • Encrypted credentials (AES-256 via crypto-js)
  • Graceful shutdown for server and worker processes

Infrastructure

  • MongoDB Atlas β€” database
  • Upstash Redis β€” serverless queue backend
  • Render β€” backend hosting
  • Vercel β€” frontend hosting
  • GitHub Actions β€” CI/CD pipeline

API surface (resources)

Resource Endpoints
Health GET /api/v1/health
Collections GET/POST /collections Β· GET/PUT/DELETE /collections/:id Β· GET /collections/:id/jobs
Jobs GET/POST /jobs Β· GET /jobs/:id Β· POST /jobs/:id/rerun Β· GET /jobs/:id/results Β· GET /jobs/:id/logs
Metrics GET /metrics Β· GET /metrics/collections/:id
Auth Profiles GET/POST /auth-profiles Β· GET/PUT/DELETE /auth-profiles/:id
Docs GET /api/v1/docs

Data model (at a glance)

  • Collection: request definitions + execution mode + optional linked auth profile
  • Job: snapshot of requests/auth + status + extracted variables + timestamps
  • RequestResult: per-request status/latency/HTTP outcome/response snippet
  • Log: structured event trail for auditing and debugging
  • AuthProfile: reusable credential metadata + encrypted token at rest

Environment variables

NODE_ENV=
PORT=5000
MONGO_URI=
REDIS_URL=
ENCRYPTION_SECRET=
WORKER_CONCURRENCY=5
INLINE_WORKER=true
FRONTEND_URL=

Running locally

# Install dependencies
npm install

# Start API server
npm run dev

# Start worker (separate terminal)
npm run worker:dev

# Run tests
npm test

Live deployment

URL
🌐 Frontend https://api-testing-platform-two.vercel.app

Repository layout

  • src/ β€” Express API, controllers, models, services, worker/queue logic
  • frontend/ β€” Next.js dashboard UI
  • tests/ β€” 27 Jest tests covering SSRF, variable extraction, validation, encryption, snapshots
  • .github/workflows/ β€” GitHub Actions CI/CD pipeline

About

Distributed API Workflow Execution Platform

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors