Complete API reference for the mongo-query-top REST API server.
http://localhost:7001
Configurable via api.port in config/default.yaml or config/local.yaml.
All endpoints (except /health) require API key authentication via header:
X-API-Key: your-api-key-hereOr via query parameter for Server-Sent Events (SSE):
?apiKey=your-api-key-hereConfigure API key in config/local.yaml:
api:
apiKey: your-secure-api-key-hereCheck if the API server is running.
Request:
GET /healthResponse:
{
"status": "ok",
"timestamp": "2025-01-03T12:00:00.000Z",
"connectedServers": ["localhost", "production"]
}No authentication required.
Get all configured MongoDB servers from config/default.yaml and config/local.yaml.
Request:
GET /api/serversResponse:
{
"servers": [
{
"id": "localhost",
"name": "Local MongoDB",
"connected": true
},
{
"id": "production",
"name": "Production Cluster",
"connected": false
}
]
}Establish a connection to a MongoDB server.
Request:
POST /api/servers/:id/connectParameters:
id- Server ID from config (e.g.,localhost,production)
Response:
{
"success": true,
"serverId": "localhost",
"serverName": "Local MongoDB"
}Error Response (404):
{
"error": "Server not found"
}Error Response (500):
{
"error": "Connection failed",
"message": "connection refused"
}Close connection to a MongoDB server.
Request:
POST /api/servers/:id/disconnectParameters:
id- Server ID
Response:
{
"success": true,
"serverId": "localhost"
}Error Response (404):
{
"error": "Server not connected"
}Get connection status for a specific server.
Request:
GET /api/servers/:id/statusParameters:
id- Server ID
Response:
{
"serverId": "localhost",
"serverName": "Local MongoDB",
"connected": true
}Fetch current MongoDB operations once.
Request:
GET /api/queries/:serverId?minTime=1&showAll=falseParameters:
serverId- Server IDminTime(optional) - Minimum query runtime in seconds (default: 1)showAll(optional) - Include system queries (default: false)
Response:
{
"queries": [
{
"idx": 1,
"opid": "shard01:12345",
"secs_running": 5,
"runtime_formatted": "5s",
"operation": "query",
"namespace": "mydb.users",
"collection": "users",
"database": "mydb",
"query": { "email": "test@example.com" },
"client": {
"ip": "192.168.1.100",
"port": 51234,
"geo": {
"country": "US",
"city": "San Francisco",
"ll": [37.7749, -122.4194]
}
},
"userAgent": "MongoDB Node.js Driver v5.0.0",
"planSummary": "COLLSCAN",
"isCollscan": true,
"waitingForLock": false
}
],
"summary": {
"totalOperations": 1,
"uniqueCollections": 1,
"uniqueClients": 1,
"collscans": 1
}
}Real-time stream of query data. Emits queries events at the specified refresh interval.
Request:
GET /api/queries/:serverId/stream?minTime=1&refreshInterval=2&showAll=false&apiKey=your-api-keyParameters:
serverId- Server IDminTime(optional) - Minimum query runtime in seconds (default: 1)refreshInterval(optional) - Fetch interval in seconds (default: 2)showAll(optional) - Include system queries (default: false)apiKey- API key for authentication (required for SSE)
Response Format:
Server-Sent Events stream:
event: queries
data: {"queries":[...],"summary":{...}}
event: queries
data: {"queries":[...],"summary":{...}}
JavaScript Client Example:
const eventSource = new EventSource(
`http://localhost:7001/api/queries/localhost/stream?apiKey=dev-key&minTime=1&refreshInterval=2`,
);
eventSource.addEventListener("queries", (event) => {
const data = JSON.parse(event.data);
console.log("Queries:", data.queries);
console.log("Summary:", data.summary);
});
eventSource.onerror = (error) => {
console.error("SSE error:", error);
eventSource.close();
};React Hook Example:
See apps/web/src/hooks/useServerSentEvents.ts for production implementation with reconnection logic.
Terminate a running MongoDB operation via killOp.
Request:
POST /api/queries/:serverId/kill/:opidParameters:
serverId- Server IDopid- Numeric operation ID (as shown in the query list)
Response:
{
"success": true,
"opid": 12345,
"result": { "ok": 1 },
"timestamp": "2025-01-03T12:00:00.000Z"
}Error Response (400): { "error": "Invalid opid" } — opid must be a positive integer.
Error Response (404): { "error": "Server not connected" }
Save one query to disk on demand (distinct from the threshold/COLLSCAN auto-save and the full snapshot below).
Request:
POST /api/queries/:serverId/saveBody:
{
"query": { "...": "processed query object" },
"type": "manual-save"
}type(optional) - Label used in the saved filename (default:manual-save)
Response:
{
"success": true,
"message": "Query saved successfully",
"timestamp": "2025-01-03T12:00:00.000Z"
}Save current queries to disk as JSON files.
Request:
POST /api/queries/:serverId/snapshot?minTime=1Parameters:
serverId- Server IDminTime(optional) - Minimum query runtime (default: 1)
Response:
{
"success": true,
"files": {
"raw": "logs/localhost/queries-raw-1704283200000.json",
"sanitized": "logs/localhost/queries-sanitized-1704283200000.json"
}
}Files are saved to logs/<serverId>/ directory.
Get all saved query snapshots for a server.
Request:
GET /api/queries/:serverId/logsParameters:
serverId- Server ID
Response:
{
"files": [
{
"name": "queries-sanitized-1704283200000.json",
"path": "logs/localhost/queries-sanitized-1704283200000.json",
"size": 12345,
"modified": "2025-01-03T12:00:00.000Z"
}
]
}Retrieve contents of a saved log file.
Request:
GET /api/queries/:serverId/logs/:filenameParameters:
serverId- Server IDfilename- Log file name (e.g.,queries-sanitized-1704283200000.json)
Response:
Returns the JSON content of the log file.
{
"queries": [...],
"summary": {...},
"timestamp": "2025-01-03T12:00:00.000Z"
}Lists live connections via $currentOp (including idle ones), so the view reflects who's connected, not just who's running an operation.
Request:
GET /api/clients/:serverId?showAll=false&readPreference=secondaryPreferredParameters:
serverId- Server IDshowAll(optional) - Include system/internal clients (default: false)readPreference(optional) - MongoDB read preference for the$currentOpaggregation
Response:
{
"clients": [
{
"ip": "192.168.1.100",
"port": 51234,
"appName": "mongosh 2.0.0",
"active": false,
"geo": { "country": "US", "city": "San Francisco" }
}
],
"summary": {
"totalClients": 1,
"uniqueIps": 1
},
"metadata": {
"serverId": "localhost",
"timestamp": "2025-01-03T12:00:00.000Z"
}
}Real-time stream of connected clients. Emits clients events at the specified refresh interval.
Request:
GET /api/clients/:serverId/stream?refreshInterval=2&showAll=false&node=host:27017Parameters: same as the one-time fetch above, plus:
refreshInterval(optional) - seconds between updates (default: 2)node(optional) - Pin sampling to a specific replica-set member so you see the clients connected to that node (e.g. a chosen secondary)
Runs the MongoDB top command and groups read/write activity by collection. Powers the dashboard's Collection Activity tab.
Request:
GET /api/top/:serverId?showAll=false&readPreference=secondaryPreferredParameters:
serverId- Server IDshowAll(optional) - Include system collections (default: false)readPreference(optional) - MongoDB read preference for thetopcommand
Response:
{
"collections": [
{
"ns": "mydb.users",
"database": "mydb",
"collection": "users",
"total": { "time": 1200, "count": 5 },
"readLock": { "time": 800, "count": 3 },
"writeLock": { "time": 400, "count": 2 }
}
],
"metadata": {
"serverId": "localhost",
"timestamp": "2025-01-03T12:00:00.000Z",
"intervalMs": 0,
"serverStartedAt": "2025-01-01T00:00:00.000Z"
}
}A one-time fetch has no previous sample to diff against, so deltas are zero. serverStartedAt is derived from serverStatus.uptime and used by the dashboard to show a live uptime readout.
Real-time per-interval stream of collection activity. Emits top events; each frame's counts are the delta since the previous sample (like mongotop), so the first frame after connecting is always zero.
Request:
GET /api/top/:serverId/stream?refreshInterval=2&showAll=false&node=host:27017Parameters:
serverId- Server IDrefreshInterval(optional) - Fetch interval in seconds (default: 2)showAll(optional) - Include system collections (default: false)readPreference(optional) - MongoDB read preferencenode(optional) - Pin sampling to a specific replica-set member (see below) so consecutive diffs are computed against the same node instead of jumping between secondaries
List replica-set members so the client can offer node-pinned sampling for the Collection Activity and Connected Clients streams.
Request:
GET /api/top/:serverId/nodesResponse:
{
"nodes": [
{ "host": "host1:27017", "role": "primary" },
{ "host": "host2:27017", "role": "secondary" }
]
}# Set your API key
export API_KEY="dev-key-change-in-production"
# List servers
curl -H "X-API-Key: $API_KEY" \
http://localhost:7001/api/servers
# Connect to a server
curl -X POST -H "X-API-Key: $API_KEY" \
http://localhost:7001/api/servers/localhost/connect
# Get current queries
curl -H "X-API-Key: $API_KEY" \
http://localhost:7001/api/queries/localhost?minTime=2
# Stream real-time updates (SSE)
curl -H "X-API-Key: $API_KEY" \
http://localhost:7001/api/queries/localhost/stream
# Or with query parameter for authentication
curl "http://localhost:7001/api/queries/localhost/stream?apiKey=$API_KEY"
# Save snapshot
curl -X POST -H "X-API-Key: $API_KEY" \
http://localhost:7001/api/queries/localhost/snapshot
# List saved logs
curl -H "X-API-Key: $API_KEY" \
http://localhost:7001/api/queries/localhost/logs
# Read specific log file
curl -H "X-API-Key: $API_KEY" \
http://localhost:7001/api/queries/localhost/logs/queries-sanitized-1704283200000.jsonimport axios from "axios";
const apiClient = axios.create({
baseURL: "http://localhost:7001",
headers: {
"X-API-Key": "dev-key-change-in-production",
},
});
// List servers
const { data: serversData } = await apiClient.get("/api/servers");
console.log(serversData.servers);
// Connect to server
await apiClient.post("/api/servers/localhost/connect");
// Get queries
const { data: queriesData } = await apiClient.get("/api/queries/localhost", {
params: { minTime: 2, showAll: false },
});
console.log(queriesData.queries);
// Save snapshot
const { data: snapshotData } = await apiClient.post("/api/queries/localhost/snapshot");
console.log("Saved to:", snapshotData.files);All error responses follow this format:
{
"error": "Error message",
"message": "Additional details (optional)"
}Common Status Codes:
200- Success401- Unauthorized (invalid or missing API key)404- Resource not found (server, log file, etc.)500- Internal server error (connection failure, etc.)
CORS is enabled for the following origins by default:
http://localhost:7000(Vite dev server)http://localhost:7010(production build, added viaconfig/production.yaml)
Configure additional origins in config/local.yaml:
api:
cors:
origins:
- http://localhost:7000
- https://my-dashboard.example.com
credentials: trueCurrently no rate limiting is implemented. For production use, consider adding rate limiting middleware.
The API currently uses Server-Sent Events (SSE) for real-time streaming. SSE is simpler and sufficient for one-way server-to-client communication. WebSocket support may be added in the future if bidirectional communication is needed.
A TypeScript API client is available in the web app:
File: apps/web/src/utils/api.ts
You can extract and reuse this client in your own applications:
import { apiClient } from "@mongo-query-top/web/utils/api";
// Already configured with base URL and API key from env vars
const { data } = await apiClient.get("/api/servers");When using SSE streams:
- Handle reconnection - Implement exponential backoff
- Close connections - Always close EventSource when unmounting
- Monitor connection state - Track
onopen,onerror,oncloseevents - Parse JSON - Event data is stringified JSON
- Use query params for auth - EventSource doesn't support custom headers
Production Example:
See apps/web/src/hooks/useServerSentEvents.ts for a production-ready React hook with:
- Automatic reconnection with exponential backoff
- Connection state management
- Error handling
- Cleanup on unmount
Start the API server in development mode:
# With hot reload
pnpm run dev:api
# Or with Turborepo
turbo dev --filter=@mongo-query-top/apiAPI server will restart automatically on file changes.
Build and run in production:
# Build
pnpm run build
# Start API server
NODE_CONFIG_DIR=./config NODE_ENV=production node apps/api/dist/server.js
# Or use npm script
pnpm run start:apiEnvironment Variables:
The API server uses config module which reads from config/default.yaml, config/production.yaml (when NODE_ENV=production), and config/local.yaml. No other environment variables are needed.
Reverse Proxy:
For production, use nginx or similar:
server {
listen 80;
server_name api.example.com;
location / {
proxy_pass http://localhost:7011;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}