Skip to content

API Contracts

This page describes the HTTP and WebSocket contracts that vite-plugin-monitor's client uses to communicate with the backend. Implement these contracts when using mode: 'http' or mode: 'websocket'.

Ingest Endpoint (HTTP)

Used when storage.mode = 'http'. The browser POSTs batched events to this endpoint.

Request

POST <storage.writeEndpoint>
Content-Type: application/json
X-Tracker-Key: <storage.apiKey>    (only when apiKey is configured)

{
  "events": TrackerEvent[]
}

Headers:

HeaderValueWhen
Content-Typeapplication/jsonAlways
X-Tracker-KeyThe configured apiKeyOnly when storage.apiKey is set

Body:

The events array contains one or more TrackerEvent objects. A single flush can contain up to storage.batchSize events (default: 25).

Response

StatusBehavior
2xx (any)Success — batch is acknowledged and removed from the client queue
Non-2xxFailure — batch is re-queued and retried on the next flush interval

Minimal success response:

HTTP/1.1 200 OK

No response body is required. The client ignores the response body for the ingest endpoint.

Page Unload

On page unload (beforeunload), the client sends remaining events via navigator.sendBeacon:

POST <writeEndpoint>
Content-Type: text/plain;charset=UTF-8

{ "events": TrackerEvent[] }

Content-Type on Beacon requests

navigator.sendBeacon sends with Content-Type: text/plain;charset=UTF-8 regardless of the data. Your backend must be prepared to parse a JSON body with this content type, or use Content-Type: application/json overriding with the Fetch Keepalive API (the plugin uses Beacon for maximum reliability on unload).


Read Endpoint (HTTP)

Used by the dashboard to query events. Required if you want the dashboard to display events in mode: 'http'.

Request

GET <storage.readEndpoint>?since=<ISO8601>&until=<ISO8601>
Accept: application/json
X-Tracker-Key: <storage.apiKey>    (only when apiKey is configured)

Query Parameters:

ParameterFormatDescription
sinceISO 8601 UTCStart of time range (inclusive)
untilISO 8601 UTCEnd of time range (inclusive)

The dashboard always sends both since and until. Your server must filter events to the [since, until] window and return them newest first (descending by timestamp).

Example request:

GET /tracker/events?since=2024-03-15T00:00:00.000Z&until=2024-03-15T23:59:59.999Z
Accept: application/json
X-Tracker-Key: tk_prod_xxxx

Response

HTTP/1.1 200 OK
Content-Type: application/json

{
  "events": TrackerEvent[],
  "total":  123
}
FieldTypeDescription
eventsTrackerEvent[]Events in the time range, newest first
totalnumberTotal count of events in the time range

Pagination

The dashboard currently does not use server-side pagination — it loads all events for the selected time range in a single request. For large time ranges (e.g. 30d with millions of events), you may want to implement server-side aggregation or return a limited sample.

The total field should reflect the full count even if you return a subset of events.

Client-side filtering

Your server only needs to implement time-range filtering (since/until). All other filtering (by type, level, userId, route, full-text search) and all aggregations (KPI cards, charts, top lists) are performed client-side in the browser dashboard. Do not implement these on the server.


Ping Endpoint (HTTP)

Used by the dashboard's health check indicator. Any URL that returns 2xx is sufficient.

GET <storage.pingEndpoint>

→ 200 OK (or any 2xx)

The dashboard polls this endpoint to determine whether the backend is reachable and shows a coloured status dot (🟢 online / 🔴 offline).

If pingEndpoint is not configured, the backend is assumed online and the status dot is hidden.


WebSocket Protocol

Used when storage.mode = 'websocket'. All messages are JSON strings sent over a single persistent WebSocket connection.

The client connects to storage.wsEndpoint (wss://...) and uses the same connection for both event ingest and dashboard queries.

Connection

When the connection is established (or re-established after a disconnect), the client does not send an explicit handshake message. The server should treat the connection as open for both ingest and query messages.

The client reconnects automatically with exponential backoff on disconnect.


Ingest — Browser → Server

The browser sends batched events:

json
{
  "type":   "ingest",
  "events": TrackerEvent[]
}

Ingest ACK — Server → Browser

The server acknowledges receipt:

json
{
  "type":  "ack",
  "saved": 42
}
FieldTypeDescription
type"ack"Message discriminant
savednumberNumber of events successfully persisted

If no ACK is received within the flush timeout, the batch is re-queued.


Dashboard Query — Dashboard → Server

The dashboard requests events for a time range:

json
{
  "type":  "events:query",
  "reqId": "550e8400-e29b-41d4-a716-446655440000",
  "query": {
    "since": "2024-03-15T00:00:00.000Z",
    "until": "2024-03-15T23:59:59.999Z"
  }
}
FieldTypeDescription
type"events:query"Message discriminant
reqIdstringUUID — correlates the response to this request
query.sinceISO 8601Start of time range
query.untilISO 8601End of time range

Dashboard Response — Server → Dashboard

The server responds to a query:

json
{
  "type":  "events:response",
  "reqId": "550e8400-e29b-41d4-a716-446655440000",
  "response": {
    "events": TrackerEvent[],
    "total":  123
  }
}
FieldTypeDescription
type"events:response"Message discriminant
reqIdstringMust match the reqId from the query
response.eventsTrackerEvent[]Events in range, newest first
response.totalnumberTotal count in range

Real-Time Push — Server → Browser (Optional)

The server can push new events to all connected clients in real time:

json
{
  "type":   "push",
  "events": TrackerEvent[]
}

When the dashboard receives a push message while in Live mode, it merges the new events into the current dataset without a full re-query. This enables true real-time updates without polling.

This message is optional — the dashboard works correctly without it (it falls back to polling).


Middleware & Standalone Mode Endpoints

In middleware and standalone modes, the plugin implements all endpoints internally. These are the auto-configured values:

EndpointMiddlewareStandalone
Ingest (POST)/_tracker/eventshttp://localhost:4242/_tracker/events
Read (GET)/_trackerhttp://localhost:4242/_tracker
Ping (GET)/_tracker/ping/_tracker/ping (also on Vite server)
WebSocketws://localhost:4242/_tracker/ws

The ping endpoint in middleware mode returns:

json
{
  "ok":    true,
  "appId": "my-app",
  "mode":  "middleware",
  "version": "0.1.0"
}

Released under the MIT License.