Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 6 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,20 +67,14 @@ Security notes:
- Replay protection is enforced by deduplicating `eventId` values in the ingestion service.
- Duplicate deliveries are treated as safe no-ops and return `202 Accepted`.

## Prometheus Metrics
## API Versioning Policy

The backend exposes a standard Prometheus metrics endpoint at `GET /metrics`.
All new features and endpoints must be mounted under the `/api/v1` prefix.

- **HTTP Metrics**: Tracks request duration, error rates, and throughput (labels: `method`, `route`, `status_code`).
- **Custom Metrics**: Includes `job_sync_lag_seconds` to track background processing delay.
- **Default Metrics**: Standard Node.js process and runtime metrics (GC, memory, CPU).

### Security

In `production` Environments, the `/metrics` endpoint is protected by a Bearer token.
- Set `PROMETHEUS_AUTH_TOKEN` in your environment.
- Clients must provide `Authorization: Bearer <token>`.
- In non-production environments, authentication is bypassed for easier debugging.
**Deprecation and Sunset Policy:**
We use HTTP headers to signal end-of-life for specific API versions:
- `X-API-Version`: Indicates the current version of the API responding to the request.
- `Deprecation`: A boolean flag (`true` or `false`) indicating if the API version is deprecated. When `true`, developers should migrate to a newer version as soon as possible.

## Scripts

Expand Down
16 changes: 16 additions & 0 deletions src/api/v1/router.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Router, Request, Response, NextFunction } from "express";
import streamsRouter from "./streams";

const v1Router = Router();

// Global middleware for v1 APIs
v1Router.use((_req: Request, res: Response, next: NextFunction) => {
res.setHeader("X-API-Version", "v1");
res.setHeader("Deprecation", "false");
next();
});

// Mount existing streams logic
v1Router.use("/streams", streamsRouter);

export default v1Router;
5 changes: 2 additions & 3 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,15 @@

import cors from "cors";
import express, { Request, Response } from "express";
import streamRoutes from "./api/v1/streams";
import { metricsMiddleware, metricsHandler } from "./metrics/prometheus";
import v1Router from "./api/v1/router";

import indexerWebhookRouter from "./routes/webhooks/indexer";

const app = express();
const PORT = process.env.PORT ?? 3001;

app.get("/metrics", metricsHandler);

Check failure on line 14 in src/index.ts

View workflow job for this annotation

GitHub Actions / build-test

Cannot find name 'metricsHandler'.
app.use(metricsMiddleware);

Check failure on line 15 in src/index.ts

View workflow job for this annotation

GitHub Actions / build-test

Cannot find name 'metricsMiddleware'.

app.use(cors());
app.use("/webhooks/indexer", express.raw({ type: "application/json" }), indexerWebhookRouter);
Expand All @@ -23,7 +22,7 @@
res.json({ status: "ok", service: "streampay-backend", timestamp: new Date().toISOString() });
});

app.use("/api/v1/streams", streamRoutes);
app.use("/api/v1", v1Router);

if (require.main === module) {
app.listen(PORT, () => {
Expand Down
Loading