Test how fast, stable and healthy your backend is.
This guide explains every screen of the studio: what it does, when to use it, how to read the results, and the technology behind it. Read it top to bottom once, then use the search box (/) to jump to what you need.
Introduction #
What the studio is, who it is for, and what it connects to.
Bongo Performance Studio is a web app that runs on your own computer (by default at http://localhost:3333). It tests any backend that serves an HTTP API, whatever language or framework it is written in, and it can inspect and seed any MongoDB database. See Supported Backends for the details.
It answers questions like: "How many users can our server handle?", "Which API is the slowest?", "Did the last release break any route?", "Are our database queries using indexes?" and "What happens when there are 100,000 records in the database?"
What you can do
Insert realistic dummy documents into any collection, tagged so they can be removed later.
Normal load, sudden spikes, stepwise stress and rate-limiter checks against any route.
One request per route: status, latency, payload size and a ready-to-run cURL command.
Same short load test on every route, ranked by p99 latency, throughput and errors.
Index coverage, full collection scans, missing reference indexes and broken references.
Saved run history, side-by-side comparison and a printable executive report with a grade.
Who it is for
- Backend developers — find slow queries and routes before users do.
- QA testers — run a regression scan of every API after each change.
- DevOps — find the breaking point of a server and size it correctly.
- Team leads — share a clear, graded report with management or clients.
What it connects to
The studio talks to two things, both set per environment on the Settings screen:
| Connection | Used for | Example |
|---|---|---|
| Target backend (Base URL) | Load tests, benchmark, API scanner, k6, login, online check | http://localhost:5000 |
| MongoDB (DB link) | Home stats, seeding, purge, inventory, index audit, connected collections | mongodb://localhost:27017/myapp |
Both are optional on their own: without a DB link the testing screens still work, and without a running backend the database screens still work. See Architecture for the full picture.
Supported Backends #
Which kinds of backend code and databases the studio can test.
Any backend that speaks HTTP
The testing screens only send HTTP requests and measure the responses. They never look at your source code, so the language and framework do not matter. If you can call it with curl, the studio can test it.
| Language | Example frameworks |
|---|---|
| JavaScript / TypeScript | Express, NestJS, Fastify, Koa, Hapi, Next.js API routes |
| Python | Django / Django REST Framework, FastAPI, Flask |
| PHP | Laravel, Symfony, CodeIgniter |
| Java / Kotlin | Spring Boot, Quarkus, Micronaut, Ktor |
| Go | Gin, Echo, Fiber, net/http |
| C# / .NET | ASP.NET Core Web API |
| Ruby | Ruby on Rails, Sinatra |
| Others | Rust (Actix, Axum), Elixir (Phoenix), serverless functions behind an HTTP endpoint |
It does not matter where the backend runs: on your computer, in Docker, on a staging server, in the cloud, or behind Nginx or a load balancer. The studio only needs a reachable http:// or https:// Base URL.
What each feature needs
| Feature | Works with | Notes |
|---|---|---|
| Load & Spike Testing | Any HTTP/HTTPS API | Sends GET requests only. |
| Endpoint Benchmark, API Scanner | Any HTTP/HTTPS API | GET, POST, PUT, PATCH and DELETE, with a JSON body taken from the API directory. |
| Cloud Testing (k6) | Any HTTP/HTTPS API | The k6 script can be edited to send any request. |
| Auth & Tokens | Token-based login | Your login route must return a token in JSON (or an accessToken cookie). The studio sends it back as Authorization: Bearer <token>. |
| Home, Seeder, Inventory, Connected Collections, Index Auditor, Purge | MongoDB only | Local, Docker or MongoDB Atlas. The backend itself can be written in any language. |
What is not covered
- Other databases. MySQL, PostgreSQL, SQL Server, Redis and others cannot be connected on the database screens. The HTTP testing screens still work for backends that use them.
- Other ways to log in. The built-in screens only send a Bearer token. API-key headers (such as
x-api-key), cookie-only sessions, HTTP Basic auth and OAuth redirect flows are not sent. To use them, set the header in a k6 script. - Non-HTTP protocols. WebSockets, gRPC and message queues are not tested by the built-in screens. k6 has its own modules for WebSockets and gRPC that you can use in a custom script, but the dashboard numbers only count HTTP requests.
- GraphQL errors. A GraphQL API can be tested as a POST route with a body such as
{"query":"{ users { id } }"}. Many GraphQL servers return errors with status 200, and the scanner judges only the status code, so those errors still count as passed.
Replace the sample defaults
A few built-in defaults are only examples. Change them to match your backend:
| Default | Where to change it |
|---|---|
Example route list (/api/v1/…) | Settings → API route directory: import or type your own routes, and delete the example rows you don't need. |
Login route /api/v1/auth/login with an email/password body | Auth & Tokens → Login Settings. |
| k6 Target Route list | Choose Custom Path in Cloud Testing. |
| Index Auditor's six preset collections | Fixed. For your own collections, use Connected Collections. |
| Student / attendance sample seeders (CLI, Seeder Defaults) | Ignore them and use the Collections Seeder, which works with any collection. |
Quick Start #
From zero to your first test in about five minutes.
Requirements
| Item | Needed for | Notes |
|---|---|---|
| Node.js 18 or newer | Everything | The project uses ES modules and Express 5. |
| A running target backend | All testing screens | Local, staging or live URL. |
| A MongoDB connection string | All database screens | Local mongod or MongoDB Atlas. |
| k6 command-line tool | Cloud Testing only | macOS: brew install k6. Other systems: see the official k6 install guide. |
Steps
-
Install and start the studio
Open a terminal in the project folder and run:
Terminalcd bongo_performance_suite npm install npm startThen open
http://localhost:3333. To use another port:UI_PORT=4000 npm start. -
Point it at your backend and database
Go to Settings & Environments, choose the Development (DEV) tab and fill in Target Backend Base URL and MongoDB Connection String. Click Test Connection, then Save Settings, then Set as Active Environment.
-
Check the top bar
The status pill should turn green and show Online (x ms). The Home cards fill with your database numbers within a few seconds.
-
Log in once (for protected APIs)
In Settings, add a Test Login Email and Password (use a dedicated test account). Then open Auth & Tokens and click Log In Now. Every test now sends this token automatically.
-
Run your first tests
Start gentle: API Health Scanner → Run Full Health Scan. Then Load & Spike Testing with 20–50 users for 15 seconds.
-
Review and share
Open Test History to compare runs, and Executive Report to produce a printable summary.
No .env file is needed. Every setting is entered on the Settings screen and saved in src/config/environments.json. If an old .env exists, its values are copied into Settings once, the first time the server starts.
Interface Tour #
The parts of the screen you will use on every page.
Top bar
| Element | What it does |
|---|---|
| Menu (☰) | Shows or hides the left sidebar. On phones and tablets the sidebar slides in over the page. |
| Target field | The backend base URL used by Load & Spike Testing and by the online check. It is filled from the active environment; you can edit it for a quick one-off test without changing Settings. |
| Environment switcher | Switches between DEV, QA and PROD. Each has its own Base URL, DB link, default token and test login. |
| Status pill | The studio pings the Target every 8 seconds. Any HTTP reply (even 401 or 404) counts as Online and shows the round-trip time. No reply within 3 seconds shows Offline. |
| Token pill | Appears once Auth & Tokens holds a token. Shows its state; click it to open Auth & Tokens. |
| Docs | Opens this documentation in a new tab, so a running test keeps its live view. |
| Gear icon | Opens Settings & Environments. |
Sidebar
- Home — dashboard overview of the database and recent runs.
- Database — Collections Seeder, Inventory, Index Auditor. (Connected Collections opens from the Seeder page.)
- Testing — Load & Spike, Endpoint Benchmark, API Scanner, Cloud Testing (k6), Test History, Auth & Tokens.
- Executive Report and Settings & Environments.
Small badges show live counts, for example the number of documents next to Inventory or saved runs next to Test History.
Live Operation Stream
At the bottom of every studio screen there is a log panel. The server pushes messages to it in real time (seeding progress, test start/finish, errors, k6 output) using Server-Sent Events. Use Clear All to empty it. If something seems stuck, look here first.
Direct links to a screen
You can open a screen directly by adding its name after # in the address bar, for example http://localhost:3333/#loadTest. The Open in Studio buttons in these docs use this.
| Link | Opens |
|---|---|
#home | Home |
#seeder · #relations · #inventory · #auditor | Database screens |
#loadTest · #benchmark · #scanner · #cloud · #history · #auth | Testing screens |
#report · #settings | Executive Report, Settings |
Safety Rules #
The studio works on real servers and real databases. Read this before you click anything big.
Know which database you are connected to. The DB link is used as-is. If your DEV environment points at a cloud database (for example MongoDB Atlas), seeding and purging change that cloud database.
The rules
- Seeding writes real documents. Every dummy document is tagged with
isPerformanceTestData: trueso it can be removed later. See Dummy Data & Purge. - Purge deletes real documents — only those that match the test-data rules, but it deletes them for good. There is no undo.
- Do not seed or load-test PROD unless it is planned and agreed. The Settings screen shows a warning banner when PROD is selected.
- Load tests create real traffic. On a shared server, other users will feel it. Start small and increase step by step.
- Rate limiters can block you. Many backends limit requests per IP, for example 500 requests per 5 minutes overall and only a few login attempts per 5 minutes. After a big test, your IP may get HTTP 429 until the limit window resets. Find out your backend's limits before testing.
- Write routes are sent for real. POST / PUT / DELETE routes in the API directory create, change or delete records when the scanner or benchmark runs them.
- Use a dedicated test account for Test Login, never a real admin or customer account.
- Settings contain secrets.
src/config/environments.jsonstores DB links, tokens and the test password in plain text. Do not commit or share it.
Safest setup: run the backend locally against a local or scratch MongoDB, test there, purge when finished.
Home Dashboard #
A live overview of the active database, the backend status and your latest runs.
Quick actions
The buttons next to the greeting jump straight to API Scanner, Executive Report and Cloud Testing (k6).
The eight KPI cards
| Card | What it means |
|---|---|
| Total Records | Documents across all collections. The line below shows how many collections exist. |
| Total Disk | Space the database uses on disk: document storage + indexes. |
| Doc Storage | On-disk (compressed) size of the documents only. |
| Index Size | On-disk size of all indexes, plus how many indexes exist. |
| Logical Data | Uncompressed size of all documents, plus the average document size. |
| Test Data | How many documents are dummy test data, and their share of the database. |
| Env & Ping | Active environment and the latest round-trip time to the backend. |
| Test Purge | Purge All opens a confirmation, then deletes every tagged dummy document. |
Storage cards
- Document Storage and Index Storage rings show each part's share of total disk. Detail opens Inventory or the Index Auditor.
- Storage Engine shows the total footprint, the uncompressed data size and the compression ratio (uncompressed data ÷ document storage).
Why can the ratio be below 1× (for example 0.1×)? MongoDB's WiredTiger engine keeps every collection and index in its own file with a minimum size, and keeps freed space after deletes for reuse. On a small or freshly purged database those files are larger than the data itself. On large databases the ratio is usually above 1× because WiredTiger compresses documents.
Insights row
- Health Overview — quick checks: backend online and ping (warns above 1.5 s), latest API scan score, latest saved load test, reference indexes and broken references (from Connected Collections). Click a row to open the related screen.
- Recent Test Runs — the newest saved runs from
reports/. Open Test History shows all of them. - Largest Collections — bar chart of the biggest collections by Documents or Disk Size.
Home uses only data that is already available. It never starts a scan or test on its own. The numbers refresh every 8 seconds.
Collections Seeder #
Fill any collection with realistic dummy documents to see how the backend behaves with big data.
Option 1: Seed all collections (1-click)
The Universal Master Seeder card adds the same number of documents to every collection in the active database (system collections and sessions are skipped).
| Volume | Documents per collection | Good for |
|---|---|---|
| Small | 200 | A quick check that seeding works |
| Medium (default) | 1,000 | Everyday testing |
| High Stress | 2,500 | Pagination and index tests |
| Extreme Stress | 5,000 | Large-dataset simulation |
With 100 collections, Medium adds about 100,000 documents. Progress appears in the Live Operation Stream.
Option 2: Seed one collection
- Find the collection
Use the filter box, or sort by documents, disk space, storage, index size, data size, name or "Most Dummy".
- Choose a count
Click a preset (100, 500, 1k, 5k) or type your own number on the collection card.
- Seed
Click the seed button on the card. Refresh reloads the list and counts.
How the dummy documents are made
The seeder does not need your schema. It reads one existing document from the collection and builds new documents with the same field names and types:
| Field in the template | Value in the dummy document |
|---|---|
| ObjectId (or 24-character id) | A new random ObjectId |
Name contains email | perf_…@bongo-test.local |
Name contains name | A fake full name with "(Test N)" |
phone / mobile | A Bangladeshi-style number +8801… |
pass | A fixed dummy hash (nobody can log in with it) |
status, role, gender, priority | A random value from a short list |
code, id, number, key, slug… | PERF_XXXXXX_… (unique) |
title / desc / message / text | A product name or a lorem sentence |
| Number | 50–5,000 for price/amount/fee, 10–500 for count/limit, else 1–10,000 |
| Boolean / Date | Random true/false · a date in the last 30 days |
| Array / nested object | Empty [] / {} |
Every document also gets isPerformanceTestData: true, createdAt and updatedAt. An empty collection (no template) gets simple documents with name, code, status and description. Documents are inserted in batches of 1,000; duplicate-key collisions are skipped and logged.
Good for volume, not for app logic. Dummy documents are valid for MongoDB but may not follow your app's rules (allowed values, required links between records). Use them to test speed, indexes and pagination, not business flows.
Their reference fields point to random ids that do not exist, so they show up as broken references in Connected Collections until you purge.
Connected Collections #
A map of how collections reference each other, with missing indexes and broken references highlighted.
How it works
The newest 20 documents of each collection are sampled. Any ObjectId (or 24-character id string) whose value exists as an _id in another collection becomes an arrow A → B: "documents in A store the id of a document in B". Results are cached for 5 minutes; Re-scan builds the map again. Nothing is written to the database.
Reading the map
| You see | Meaning |
|---|---|
| Solid arrow | A reference between two collections. Click a collection to highlight what it References and what it is Referenced by. |
| Dashed arrow | No index: the reference field has no index that starts with it. Lookups on that field scan the whole collection. |
| Red arrow | Broken refs: some documents point to records that no longer exist. |
| Counters | Collections, connected, relationships, unindexed and broken totals. |
Tools on this screen
- Find a collection — search and focus a node. Fit to Screen resets the view.
- Check Broken References — counts documents whose reference points to a missing record (read-only
$lookup). - Collection Details — click a node to see which fields link it to other collections.
- Index & Integrity Findings — lists unindexed reference fields with a ready
createIndexcommand, and broken-reference counts. - Not Connected — collections with no detected links in either direction.
A reference field that is empty in all 20 sampled documents can be missed. The map shows what the data says, not what the code intends.
Collections Inventory #
One table with the size of every collection, split into dummy and real data.
Summary tiles at the top show total collections, documents, disk space, document storage, index size and uncompressed data. The table below has one row per collection:
| Column | Meaning |
|---|---|
| Documents | Number of documents in the collection. |
| Total Disk Space | Document storage + index size on disk. |
| Doc Storage / Index Size | The two parts of the disk space. |
| Data Size (Avg) | Uncompressed size, with the average document size. |
| Dummy Test / Real Data | How many documents are tagged test data vs everything else. |
| Distribution | A bar showing the dummy vs real share. |
Use the search box to filter by name and Refresh after seeding or purging.
Index Auditor #
Checks whether typical queries use an index or scan the whole collection.
What "Run Live DB Audit" does
The auditor works on a fixed preset of six collections. For each one it lists every index (name, keys, unique) and runs one typical query with MongoDB's explain("executionStats"):
| Preset collection | Field the test query filters on |
|---|---|
| users | email (exact match) |
| students | studentId (exact match) |
| teachers | teacherId (exact match) |
| attendances | date (last 30 days) |
| feeinvoices | status (exact match) |
| academicclasses | classCode (exact match) |
Reading the result
- IXSCAN (index scan) — good. MongoDB jumped straight to the matching entries.
- COLLSCAN (collection scan) — slow. MongoDB read every document because no index matched.
- Docs examined vs keys examined — many documents examined for few results means a missing or poor index.
- Health score — the share of the six collections whose query did not do a COLLSCAN.
Below the results you get best-practice recommendations (connection pool size and compound indexes for the preset collections) with copy-ready code.
The six collection names are fixed. If your database does not have them, they show 0 documents and are not explained. To find missing indexes in any database, use Connected Collections: it lists every reference field without an index, with a ready createIndex command.
Load & Spike Testing #
Send many requests to one route at the same time and measure speed and failures.
The four scenarios
| Scenario | What happens | Use it to |
|---|---|---|
| Standard Load Test | The chosen number of users send requests non-stop for the chosen time. | Measure normal peak performance. |
| Sudden Traffic Spike | All users start at once, with a 10-second timeout per request. Watch the drop rate (5xx + timeouts). | See if the server survives a burst, e.g. result publishing. |
| Breaking Point Stress | Four stages at 20%, 50%, 80% and 100% of the users; each stage gets a quarter of the duration (at least 3 s). Each stage's numbers appear in the Live Operation Stream; the scorecard shows the overall result with the worst p95. | Find where latency and errors start to climb. |
| Rate Limiter Audit | Ignores the sliders and the token. Sends 550 GET requests with 25 parallel workers and counts HTTP 200 vs 429, plus the request number of the first 429. The counts are printed in the terminal where npm start runs; the scorecard is not filled for this scenario. | Confirm that express-rate-limit blocks abuse. |
How to run a test
- Pick a scenario
Click one of the four scenario cards.
- Set users and duration
Concurrent Virtual Users: 5–500. Duration: 5–60 seconds. Start with 20–50 users.
- Choose the route
Pick from Select API Route to Hit (filled from the API directory in Settings) or choose Custom Endpoint Path. You can edit the path and add query parameters, e.g.
/api/v1/users?page=1&limit=20. - Add a token if the route is protected
The Bearer Token field is filled automatically from Auth & Tokens. Without a token, protected routes answer 401 and the test measures only the rejection.
- Launch
Click Launch Load Test. The scorecard shows the result when the run finishes.
Results scorecard
| Number | Meaning |
|---|---|
| Requests / Sec | Average requests the server completed per second. Higher is better. |
| Avg Latency | Average time from request to response. |
| p95 Latency | 95% of requests were faster than this. The best single number for "how slow does it feel". |
| Errors / Drop Rate | Server errors (5xx) plus timeouts. Should be 0. |
GET only. This screen always sends GET, even if the route in the directory is POST. Use the API Scanner or Endpoint Benchmark for other methods.
Saved runs: Standard Load and Spike results are saved to reports/ and appear in Test History. Stress and Rate Limiter results are shown on screen and in the log only.
The Rate Limiter Audit is meant to trigger the limiter. Afterwards your IP can receive 429 responses until the limiter window resets (often several minutes), which also affects other tests. 550 requests are enough to pass a limit of 500; if your backend allows more, the audit shows no 429.
Endpoint Benchmark #
Run the same short load test on every route and rank them to find the slow ones.
Settings
| Field | Meaning |
|---|---|
| Target Base URL | Taken from the top bar unless you edit it here. |
| Users per route | 1–100 concurrent users for each route. |
| Seconds per route | 3, 5, 10 or 15 seconds per route. |
| Routes | How many routes from the API directory will run. |
| Include write routes | Off by default: only GET routes run. When on, POST / PUT / DELETE routes run too, with their saved body. |
Routes run one after another, so the total time is about routes × seconds per route (40 routes × 5 s ≈ 3.5 minutes). Duplicate method + path pairs run once. The token from Auth & Tokens is used automatically. Stop ends the run after the current route; finished routes are still saved.
Results
Routes are ranked by p99 latency (slowest first). Each row shows requests/sec, average, p50 / p90 / p99 and max latency, errors (network errors + timeouts), non-2xx responses and the 2xx / 4xx / 5xx split. Every run is saved as reports/endpoint_benchmark_*.json; you can reopen and compare runs in Test History.
Include write routes sends real POST / PUT / DELETE requests many times per second: POST creates records, PUT changes them and DELETE removes them. Only use it against a test database.
API Health Scanner #
One request to every route: is it up, what status does it return, how fast is it?
How to run a scan
- Check the target and token
Target Base URL follows the active environment. The Bearer Token is filled from Auth & Tokens.
- Choose speed
Concurrency: 1, 3, 5 (recommended) or 10 parallel workers. Timeout: 3, 6 or 10 seconds per request.
- Run Full Health Scan
Every route in the API directory is called once, with its method and saved body. Stop Scan cancels it.
Reading the results
| Item | Meaning |
|---|---|
| Health Score | Passed routes ÷ scanned routes. A route passes when it returns HTTP 2xx or 3xx. |
| Endpoints Scanned | Total, passed and failed. |
| Avg Latency | Average response time, with min and max. |
| Status Breakdown | Count of 2xx, 4xx and 5xx, and the failure rate. |
| Filters | All, Passing, Failing, and Slow (> 300 ms). Search by path or name. |
| Reproduction | A ready cURL command for each route, so you can repeat the exact request in a terminal. |
Special status values
- 0 · Connection Refused — nothing is listening at the Base URL. Is the backend running?
- 408 · Request Timeout — no answer within the timeout.
- 500 · Network Error — another network problem (DNS, TLS…). Check the message; this is not necessarily a real server 500.
- 401 / 403 — usually a missing or expired token. Log in again in Auth & Tokens.
Export JSON downloads the full result. Each scan is saved to Test History with the token replaced by <token>, and the latest scan feeds the Executive Report.
Cloud Testing (k6) #
Run Grafana k6 load-test scripts from the studio, with Grafana-style panels and a Prometheus metrics endpoint.
k6 must be installed on the computer that runs the studio server (brew install k6 on macOS). The test runs locally with k6 run; "Cloud" refers to the k6 / Grafana tooling, nothing is uploaded to Grafana Cloud.
How to run a k6 test
- Pick a preset
The preset sets the number of virtual users (VUs) and the duration (table below).
- Pick the target route
Choose a route or Custom Path. The host comes from the top-bar Target field.
- Check the token
The Authorization Bearer Token is filled from Auth & Tokens and written into the script.
- Review the script
The editor shows the exact k6 JavaScript that will run. You can edit it.
- Run k6 Cloud Test
The k6 Engine Log tab shows k6's output. Abort Test stops it.
Presets
| Preset | VUs | Duration | Purpose |
|---|---|---|---|
| Smoke Test | 5 | 10 s | Sanity check that the route answers correctly. |
| Standard Load Test | 100 | 30 s | Typical peak traffic. |
| Traffic Spike Test | 400 | 20 s | Sudden burst. |
| Stress & Breaking Point | 500 | 40 s | Maximum capacity. |
| Soak / Endurance | 50 | 60 s | Steady load to reveal memory or connection leaks. |
| Multi-Step API User Flow | 100 | 30 s | Several routes in one user journey. |
What actually runs
The script in the editor is exactly what k6 runs. The studio writes a simple script for you: the chosen VUs for the whole duration, one GET request to the route, a check that the status is 200, and a threshold of p95 < 500 ms.
- Changing the preset, route or token, or clicking Reset Script, rewrites the script and replaces your edits. Make manual edits last.
- Moving the VUs or Duration slider does not change the script by itself. Click Reset Script afterwards to apply the new values.
- To run the built-in staged version of a preset (ramp up → hold → ramp down, and the multi-step flow), clear the editor completely before clicking Run. The server then uses its own template for that preset.
- Export .js downloads the script so you can run
k6 run test.jsyourself or in CI.
Dashboard panels
The six KPI cards (VUs, throughput, p95, p99, error rate, checks passed) come from k6's own end-of-test summary, so they are exact. An error rate under 1% is shown as SLA Healthy.
The four charts are an approximation. k6's summary has no per-second data, so the charts are drawn after the run as a smooth curve shaped from the final p95 and requests/sec. Read them as an illustration, not as measurements. Likewise, failed requests are split into 4xx / 5xx by an estimate; use the API Scanner for exact status codes.
Prometheus and PromQL
- Raw /metrics Stream shows the studio's own endpoint
http://localhost:3333/metricsin Prometheus text format: request counts by status, latency quantiles from the last k6 run, current/peak VUs, and the studio process's CPU, heap memory and uptime. A real Prometheus server can scrape it. - PromQL Explorer is a simplified lookup, not a full PromQL engine. It recognises the words
http_requests_total,duration/latency,vus,cpuandheap/memand returns the matching current values. Functions such asrate()are not calculated.
Each run is saved to Test History, and the latest run feeds the Executive Report.
Test History & Compare #
Every saved run in one table, with side-by-side comparison to spot regressions.
What is saved
| Kind | Comes from | File name |
|---|---|---|
| Load & Spike | Standard Load and Spike tests (and CLI smoke/load/spike) | standard_load_test_*.json, traffic_spike_test_*.json |
| Endpoint Benchmark | Endpoint Benchmark | endpoint_benchmark_*.json |
| k6 Cloud | Cloud Testing | k6_*.json |
| API Scan | API Health Scanner | api_health_scan_*.json |
Using the table
- Filter by kind or search by route; Refresh reloads the folder.
- Columns: run, when, setup, requests/sec, average, p99, problems, status and actions.
- View opens the full details of a run. Delete removes the file from
reports/(it asks first).
Comparing two runs
- Tick two runs
They must be the same kind: two load tests (k6 runs count as load tests here), two benchmarks or two API scans.
- Click Compare
A is the older run, B the newer one. You see the change in each number; for benchmarks per route, for scans which routes changed status.
Run the same test with the same settings before and after a change. Any difference is then caused by the change, not by different test settings.
Auth & Tokens #
Log in once and every test uses the same token automatically.
Login settings (per environment)
| Field | Meaning |
|---|---|
| Login endpoint | Your backend's login route. The default /api/v1/auth/login is only an example. |
| Method | POST, PUT or GET. |
| Request body (JSON) | May use {{TEST_ADMIN_EMAIL}} and {{TEST_ADMIN_PASSWORD}}. The server replaces them with the Test Login Email / Password from Settings. Only these two placeholders are allowed. |
| Token location | auto finds accessToken, access_token, token, jwt or idToken in the response (or an accessToken cookie). Or give a path such as data.accessToken. |
{
"email": "{{TEST_ADMIN_EMAIL}}",
"password": "{{TEST_ADMIN_PASSWORD}}"
}
The current token
- Log In Now calls the login route and keeps the token in the studio server's memory for the active environment, with its expiry time (read from the JWT).
- You can copy it, Clear Token, or Paste a token instead → Use This Token.
- The token is lost when the studio server restarts; log in again.
Where the token is used
The token fields of Load & Spike Testing, API Health Scanner and Cloud Testing (k6) are filled automatically, unless you typed a different token there. Endpoint Benchmark uses it directly. Before a test, the studio logs in again only if the token expires within one minute.
Login routes are often rate-limited strictly, for example 5 attempts per 5 minutes per IP. Do not click Log In Now repeatedly; if you get HTTP 429, wait until the limit resets.
Executive Report #
A one-page, graded summary you can print, save as PDF or paste as Markdown.
Where the numbers come from
| Report part | Source |
|---|---|
| Database architecture | Live database stats (disk, storage, indexes, records, compression). |
| Load test scorecard | The latest k6 run from Cloud Testing. |
| API health scorecard | The latest API Health Scan. |
Run a k6 test and an API scan first. Load & Spike (autocannon) results are not used. The latest k6 run and scan are kept in server memory, so after a studio restart run them again.
Without them the load and API parts are empty, and the grade can look better than it really is.
How the grade is calculated
The score starts at 100 and loses points:
- error rate × 10 (for example 2% errors = −20)
- p95 latency above 300 ms: −5 · above 500 ms: −15 · above 1,000 ms: −25
- (100 − API health score) × 0.5
| Grade | Rule | Verdict |
|---|---|---|
| A+ | Score ≥ 95, 0% errors and p95 < 300 ms | Production ready |
| A | Score ≥ 85 | Production capable |
| B | Score ≥ 70 | Acceptable for staging |
| C | Score ≥ 55 | Needs optimisation |
| F | Below 55 | Critical remediation required |
Recommendations are added by rule: high error rate, p95 above 500 ms, more than 5,000 records (pagination advice), plus two general tips (caching and CI regression tests).
Sharing
- Generate Fresh Report — builds the report from the current data.
- Copy Markdown — paste into GitHub, Slack, Notion or an email.
- Print / Save as PDF — the print layout hides the sidebar and controls; choose "Save as PDF" in the print dialog.
Settings & Environments #
Targets, database links, test login, seeder defaults and the API route directory.
Environments (DEV, QA, PROD)
| Field | Meaning |
|---|---|
| Target Backend Base URL | Where load tests and API calls go, e.g. http://localhost:5000. |
| Environment Display Label | Friendly name shown in the top-bar switcher. |
| MongoDB Connection String | Used for stats, seeding, purge, inventory and audits. |
| Default Bearer Token | Optional fallback token for protected routes. |
| Test Login Email / Password | Used by Auth & Tokens as {{TEST_ADMIN_EMAIL}} / {{TEST_ADMIN_PASSWORD}}. |
Test Connection pings the Base URL and opens a short test connection to MongoDB (reports the number of collections). Save Settings stores the tab; Set as Active Environment makes it the one every screen uses.
Seeder defaults
Insert Batch Size, Default Student Count and Default Attendance Count are used by the built-in student/attendance sample seeders and the command-line tools. The Collections Seeder does not use them.
API route directory
The list of routes used by Load & Spike Testing, Endpoint Benchmark and the API Scanner. Add routes in two ways:
Option 1 — upload files
Drag & drop or browse: Excel (.xlsx, .xls), .csv, .json or .txt. Several files at once; every sheet is read. Download the Excel, CSV or JSON template to start.
| Column | Required | Example / notes |
|---|---|---|
| Path | Yes | /api/v1/users — full URLs and ?query are fine |
| Method | No | GET, POST, PUT or DELETE (empty = GET) |
| Name | No | Label shown in the route dropdowns |
| Category | No | Groups routes (empty = the sheet name) |
| Body | No | JSON sent with POST / PUT, e.g. {"name":"Test"} |
The first row must be the column headers. Rows already in the list (same method + path) are skipped.
Option 2 — type them in
One path per line, pick the method and an optional category, add a JSON body for POST / PUT, then Add Routes. The table below lists all routes; filter, delete single routes, or Reset to Default Routes.
How settings are saved
- On the server in
src/config/environments.json. - A copy is also saved in this browser (localStorage key
bongoStudio.settings). When the studio loads and the server's settings differ, the browser copy is pushed back to the server — the browser copy wins.
If you edit environments.json by hand and your change disappears after a reload, the browser copy overwrote it. Save the change through the Settings screen instead, or clear this site's data in the browser.
Recommended Workflows #
Step-by-step recipes for the most common jobs.
Before every release (regression check)
- Log in
Auth & Tokens → Log In Now.
- Scan
API Health Scanner → Run Full Health Scan. Target: 100% health.
- Compare
Test History → tick this scan and the previous one → Compare. Investigate any route that changed from passing to failing.
- Load
Cloud Testing → Standard Load preset on the busiest route.
- Report
Executive Report → Generate → Print / Save as PDF.
Find and fix slow endpoints
- Benchmark
Endpoint Benchmark, GET only, 10 users, 5 seconds per route.
- Pick the worst
Look at the top of the list (highest p99).
- Check the database
Index Auditor for COLLSCANs; Connected Collections for unindexed reference fields (copy the
createIndexcommand). - Fix and re-run
Add the index or pagination, run the same benchmark, compare in Test History.
Prepare for a traffic peak (launch, sale, big announcement)
- Seed realistic volume
Collections Seeder → bring key collections to the size you expect.
- Normal load
Load & Spike → Standard Load at your expected number of users.
- Spike
Sudden Traffic Spike at 3–5× that number. Drop rate should stay near 0%.
- Find the limit
Breaking Point Stress. Note the stage where p95 passes 1 s or errors appear: that is your capacity.
- Clean up
Home → Purge All.
Monthly database health check
- Inventory
Which collections grow fastest? Is index size reasonable?
- Index Auditor
Any COLLSCAN?
- Connected Collections
Re-scan, then Check Broken References. Fix unindexed and broken references.
Reading the Metrics #
What each number means and what counts as good.
Good, watch, bad
These are the thresholds the studio itself uses for grades and warnings.
p95 latency
Error rate
API health score
Glossary
| Term | Plain meaning |
|---|---|
| RPS (requests/sec) | How many requests the server finished per second. Throughput. |
| Latency | Time from sending a request to receiving the response. |
| Average | Mean latency. Hides slow outliers, so also look at p95 / p99. |
| p50 / p90 / p95 / p99 | Percentiles. p95 = 300 ms means 95 of 100 requests were faster than 300 ms. p99 shows the slowest 1%. |
| Concurrent users / VUs | How many simulated users send requests at the same time. Each waits for its response before sending the next. |
| Error / drop rate | Share of requests that failed (5xx or timeout). |
| 2xx / 3xx / 4xx / 5xx | Success / redirect / client error (bad request, 401 unauthorised, 404 not found) / server error. |
| 429 Too Many Requests | The rate limiter blocked the request. |
| Timeout | No response within the time limit. |
| k6 check | A pass/fail test inside a k6 script, e.g. "status is 200". |
| k6 threshold | A rule for the whole run, e.g. p95 < 500 ms. k6 marks the run failed if it is broken. |
| Spike / stress / soak | Sudden burst / step-by-step increase to the limit / steady load for a long time. |
| SLA | Service level agreement: the promised performance, e.g. "under 1% errors". |
| Index | A sorted lookup structure that lets MongoDB find documents without reading all of them. |
| IXSCAN / COLLSCAN | Query used an index / query read the whole collection. |
| Broken reference | A document stores the id of a record that no longer exists. |
Dummy Data & Purge #
How test documents are recognised, counted and removed.
What counts as test data
A document counts as test data (and will be purged) if any of these is true. The first rule covers everything the Collections Seeder creates; the others match the ids and codes written by the built-in sample seeders.
| Field | Rule |
|---|---|
isPerformanceTestData | is true (set on everything the studio seeds) |
studentId / customStudentId | starts with PERF_STU_ |
teacherId | starts with PERF_TEA_ |
staffId | starts with PERF_STAFF_ |
email | ends with @bongo-test.local |
classCode | starts with CLS_ |
subjectCode | starts with SUB_ |
schoolCode | starts with SCH_PERF_ |
The CLS_ and SUB_ rules match by prefix only. If your real data uses class codes starting with CLS_ or subject codes starting with SUB_, Purge will delete them too. Check Inventory's "Dummy Test" column before purging.
Purging
- Open the purge dialog
Home → Test Purge card → Purge All.
- Confirm
Yes, Purge All Test Data runs
deleteManywith the rules above on every collection. - Verify
The Test Data card should show 0. Inventory's "Dummy Test" column should be empty.
From the terminal: npm run clean does the same.
Architecture #
How the pieces fit together.
- index.html · app.js
- Chart.js · Cytoscape
- EventSource for live logs
- autocannon: load, spike, stress, benchmark
- axios: scanner, rate limiter, login
- k6 CLI: Cloud Testing
- Mongoose: stats, seeding, audits
- Event bus → live stream
What happens when you click a button
- The browser calls the studio server
For example
POST /api/test/runwith the route, users and duration. - The server runs the engine
autocannon, axios, k6 or Mongoose does the real work against the target backend or MongoDB. The browser never talks to the target directly, so there are no CORS problems.
- Progress streams back live
The engine writes to an internal event bus;
GET /api/eventsstreams it to the Live Operation Stream with Server-Sent Events. - The result is returned and saved
The response carries the result for the screen; most test kinds are also written to
reports/for Test History.
Frameworks & Libraries #
Everything the studio is built with, and what each part does here.
Server (Node.js, from package.json)
| Library | Version | Role in the studio |
|---|---|---|
| Node.js (ES modules) | 18+ | Runtime for the server and the CLI. |
| Express | 5.x | Web server: serves the UI, the REST API and the live event stream. |
| Mongoose | 8.x | MongoDB connection: stats, seeding, purge, index audit, relationship map. |
| autocannon | 7.x | Fast HTTP load generator for load, spike, stress, smoke and the endpoint benchmark. |
| k6 (external CLI) | installed separately | Grafana's scriptable load-testing tool, used by Cloud Testing. |
| axios | 1.x | HTTP client for the API scanner, rate-limiter audit, online checks and login. |
| @faker-js/faker | 9.x | Generates realistic names, numbers, dates and text for dummy data. |
| xlsx (SheetJS) | 0.18.x | Reads and writes Excel/CSV files for the API route directory. |
| bcryptjs | 3.x | Hashes the default password of users created by the multi-tenant sample seeder. |
| cors | 2.x | Allows cross-origin calls to the studio API. |
| dotenv | 16.x | Only for the one-time import of an old .env file. |
| chalk · ora · cli-table3 | 5 · 8 · 0.6 | Colours, spinners and tables for the terminal menu (CLI). |
Web UI (loaded from CDNs, no build step)
| Library | Role |
|---|---|
| Vanilla JavaScript | All screen logic in app.js; no framework, nothing to compile. Edit and reload. |
| Tailwind CSS (Play CDN) | Utility classes for layout and spacing in the markup. |
| style.css | The studio's design system: cards, KPIs, buttons, chips, tables, gauges (plain CSS). |
| Lucide | The line icons. |
| Chart.js | Home "Largest Collections" chart and the k6 dashboard charts. |
| Cytoscape.js + dagre | Draws and lays out the Connected Collections map. |
| SheetJS (browser) | Reads uploaded Excel/CSV files and builds the downloadable templates. |
| Roboto · Roboto Mono | Fonts (Google Fonts). |
| Server-Sent Events | Browser-native live stream for logs and progress. |
Because the UI libraries come from CDNs, the studio needs internet access in the browser. Charts and the relationship map can take a second or two to appear on a slow connection.
Project Structure #
Where to find things in bongo_performance_suite/.
Where Data Is Stored #
What survives a restart and what does not.
| Location | Contents | Lifetime |
|---|---|---|
src/config/environments.json | Environments, DB links, default tokens, test login, seeder defaults, API directory, login settings | Permanent (plain text, contains secrets) |
Browser localStorage bongoStudio.settings | Copy of the settings | Per browser, until site data is cleared |
reports/*.json | Saved load, spike, benchmark, k6 and scan runs | Until deleted in Test History |
| Server memory | Auth tokens, latest k6 run (and /metrics values), latest API scan, relationship cache (5 min), stats cache (10 s) | Lost when the studio server restarts |
| System temp folder | The k6 script and summary during a run | Deleted when the run ends |
Not stored anywhere: Load & Spike stress/rate-limiter results, Index Auditor results and the Executive Report itself (generate it again any time).
Server API Reference #
The studio's own REST API (on port 3333). The UI uses these; you can call them from scripts too.
Live stream, environments and stats
| Method | Path | Purpose |
|---|---|---|
| GET | /api/events | Server-Sent Events: log, progress, metrics |
| GET | /api/environments | All environments and the API directory |
| POST | /api/environments/active | Switch the active environment |
| POST | /api/environments/update | Update fields of one environment |
| POST | /api/settings/global | Seeder defaults |
| POST | /api/environments/test-connection | Ping a Base URL and test a DB link |
| POST | /api/environments/apis/import · /delete · /clear | Manage the API directory |
| GET | /api/stats | Backend online check + database and dummy-data stats |
Database
| Method | Path | Purpose |
|---|---|---|
| GET | /api/collections/dynamic | Collections in the active database |
| GET | /api/collections/relations | Relationship map (?force=true skips the cache) |
| GET | /api/collections/relations/summary | Cached summary for Home |
| GET | /api/collections/relations/orphans | Broken-reference counts |
| POST | /api/seed/master | Seed all collections (volume) |
| POST | /api/seed/generic/:collection | Seed one collection (count) |
| POST | /api/seed/:entity | Built-in sample seeders for a preset schema (students, attendance, teachers, …) |
| POST | /api/clean | Purge all test data |
| GET | /api/audit | Run the index audit |
Testing and reports
| Method | Path | Purpose |
|---|---|---|
| POST | /api/test/run | Load / spike / stress / rate-limit / smoke test |
| POST | /api/benchmark/run · /abort | Endpoint Benchmark |
| GET | /api/benchmark/status | Is a benchmark running? |
| POST | /api/scanner/run · /abort | API Health Scanner |
| GET | /api/scanner/latest | Latest scan summary |
| GET | /api/k6/templates · /api/k6/status | k6 presets and state |
| POST | /api/k6/run · /api/k6/abort | Run or stop a k6 test |
| GET | /metrics | Prometheus text format |
| POST | /api/promql/query | Simplified PromQL lookup |
| GET | /api/history · /api/history/:file | Saved runs list / one run |
| GET | /api/history/:file/download | Download a saved run |
| DELETE | /api/history/:file | Delete a saved run |
| GET | /api/reports/recent | Recent runs for Home |
| POST | /api/report/generate | Build the Executive Report |
| GET | /api/auth-token | Token state and login settings |
| POST | /api/auth-token/config · /login · /manual · /clear | Manage the token |
curl -X POST http://localhost:3333/api/test/run \
-H "Content-Type: application/json" \
-d '{"type":"load","targetUrl":"http://localhost:5000/api/v1/users","connections":50,"duration":20}'
Command Line (CLI) #
Every core action is also available from the terminal, using the active environment's settings.
| Command | What it does |
|---|---|
npm start | Starts the web studio on port 3333 (same as npm run ui / npm run dev). |
npm run menu | Interactive terminal menu with 12 numbered actions (same as npm run cli). |
npm run seed | Built-in sample seeders: students + attendance (defaults from Settings). |
npm run seed:students | Sample students only. Custom count: node src/seeder/studentSeeder.js 5000. |
npm run seed:attendance | Sample attendance logs only. |
npm run clean | Purge all test data (same rules as Purge All). |
npm run test:smoke | 10 users for 10 s against the Base URL. |
npm run test:load | 50 users for 20 s. |
npm run test:spike | 300 users for 15 s. |
npm run test:stress | Four stages up to 500 users. |
npm run test:ratelimit | 550 requests, counts 429 responses. |
npm run audit:db | Index audit in the terminal. |
npm run test:full | Smoke, load, spike and rate-limiter tests one after another (stops if the smoke test fails). |
Deployment Templates #
Ready examples for scaling the backend after testing shows its limits.
deploy_templates/nginx/nginx.conf: load balancer with least_conn over backend instances on ports 5001–5003, plus request rate limiting (50 req/s per IP, burst 30).
deploy_templates/pm2/ecosystem.config.cjs: cluster mode, one instance per CPU core, restart when memory passes 1 GB.
deploy_templates/docker/docker-compose.yml: Nginx load balancer, two backend containers and Redis.
The templates are written for a Node.js backend. Before using them, change the build folder, start script and ports to your own project. For backends in other languages, the Nginx file still applies; PM2 and the Compose file need your own start command or image.
Typical loop: stress-test one instance → note the breaking point → run more instances behind Nginx or PM2 → stress-test again and compare in Test History.
Troubleshooting #
Common problems and how to fix them.
| Problem | Likely cause | Fix |
|---|---|---|
| Status pill shows Offline | The backend is not running, or the Target URL is wrong. | Start the backend; check the Target field and Settings → Test Connection. |
| Home cards stay at 0 | No or wrong DB link, or the database is empty. | Settings → MongoDB Connection String → Test Connection → Save → Set as Active. |
| Every request returns 401 / 403 | No token, or it expired. | Auth & Tokens → Log In Now, then run the test again. |
| Login fails with 429 | The backend's login rate limit was hit. | Wait until the limit resets; avoid repeated logins. |
| Many 429s in a load test | The global rate limiter is blocking your IP. | Wait for the window to reset, or test against a local backend. |
| Load test shows 0 requests | Nothing answered at the route URL. | Check the backend is up and the path is correct. |
| Cloud Testing error "spawn k6 ENOENT" | k6 is not installed or not on the PATH. | brew install k6, then restart the studio server. |
| "Another k6 test is already running" | A previous run is still active. | Click Abort Test and try again. |
| Executive Report shows no load data | No k6 run since the server started. | Run a Cloud Testing preset and an API scan, then regenerate. |
| Hand edits to environments.json disappear | The browser's saved copy is re-applied on load. | Edit through the Settings screen, or clear this site's data. |
| A screen says "Restart the studio server" | The server is running older code than the UI. | Stop it (Ctrl + C) and run npm start again. |
| Charts or the map do not appear | CDN scripts load slowly or are blocked. | Wait a moment; check internet access and ad-blockers. |
| Port 3333 already in use | Another studio instance is running. | Close it, or start with UI_PORT=4000 npm start. |
When in doubt, look at the Live Operation Stream at the bottom of the studio and the terminal where npm start runs. Errors are logged in both.
FAQ #
Short answers to common questions.
Which backends can I test?
Any backend that serves an HTTP API, in any language or framework: Node.js, Python, PHP, Java, Go, .NET, Ruby and more. The database screens need MongoDB. See Supported Backends for the full list and the limits.
Is it safe to run against production?
Read-only screens (Home, Inventory, Index Auditor, Connected Collections, a GET-only scan) are safe. Load tests affect real users, and seeding/purge change real data. Only do those on production with a plan and approval.
Will Purge delete my real data?
Only documents matching the test-data rules are deleted. Real records are safe unless they happen to match a rule, such as a class code starting with CLS_. See Dummy Data & Purge.
What is the difference between Load & Spike Testing and Cloud Testing (k6)?
Load & Spike uses autocannon: quick, no installation, GET only, simple scenarios. Cloud Testing uses k6: scriptable in JavaScript, checks and thresholds, exportable for CI, and it feeds the Executive Report. k6 must be installed.
How many users should I test with?
Start with your real peak (for example the number of people active at the same time during your busiest hour), then try 3–5× for a spike. "Concurrent users" here send requests without pause, so 50 test users are much heavier than 50 real people.
Why are my results different every time?
Network, caches, other traffic and your own computer all add noise. Run each test two or three times with the same settings and compare averages. Test from a machine that is not busy with other work.
Can I run the k6 script without the studio?
Yes. Click Export .js in Cloud Testing and run k6 run file.js in a terminal or a CI pipeline (GitHub Actions, GitLab CI).
How do I add or update this documentation?
Edit src/public/docs.html. Each <section class="doc-section"> becomes a sidebar entry target, and its <h3> headings become the "On this page" list and search results automatically. Reload the page to see changes; no restart is needed.