Skip to content

feat(api): add POST /streams/events ingest endpoint and SDK publishEvent - #549

Merged
Xhristin3 merged 2 commits into
XStreamRollz:mainfrom
Gracora:fix/issue-514-stream-events-ingest
Aug 24, 2026
Merged

feat(api): add POST /streams/events ingest endpoint and SDK publishEvent#549
Xhristin3 merged 2 commits into
XStreamRollz:mainfrom
Gracora:fix/issue-514-stream-events-ingest

Conversation

@Gracora

@Gracora Gracora commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Summary

Closes #514

Adds an API-key-guarded POST /streams/events ingest endpoint that queues stream events for the processing worker, plus a matching publishEvent method on the SDK client. The single most important design decision: the endpoint reuses the existing pending-events repository path (insertPendingEvent) that the worker already drains, so ingest and the existing event lifecycle share one queue rather than introducing a parallel mechanism.

Why

The platform could only produce stream events through the API's internal flows; external producers (mobile/web SDKs, server-side integrations) had no authenticated entry point to submit events. The obvious shortcut — a JWT-guarded endpoint — would have coupled event ingestion to end-user auth, which is wrong: ingest is a machine-to-machine surface. The endpoint is guarded by an API key (X-Stream-Api-Key header, STREAM_API_KEY env) instead, keeping it distinct from user auth while still authenticated.

What was built

File What it contains
api/src/streams/dto/ingest-stream-event.dto.ts IngestStreamEventDto: streamId (numeric string), eventType, payload, occurredAt, optional clientEventId. Validation via class-validator. Has matching tests in streams.service.spec.ts / stream-api-key.guard.spec.ts.
api/src/streams/stream-api-key.guard.ts StreamApiKeyGuard — checks X-Stream-Api-Key against STREAM_API_KEY; throws UnauthorizedException when missing/mismatched/unset. Has matching stream-api-key.guard.spec.ts.
api/src/streams/repository/streams.repository.ts Added insertPendingEvent + getPendingEvents to the in-memory repository (previously only on the DB repo).
api/src/streams/repository/streams-db.repository.ts insertPendingEvent — persists into stream_data with a unique constraint on client_event_id.
api/src/streams/streams.service.ts ingestEvent() — validates payload shape, dedupes on clientEventId, delegates to repository.
api/src/streams/streams.controller.ts POST /streams/events handler with Swagger docs, guarded by StreamApiKeyGuard.
api/src/streams/streams.module.ts Registered StreamApiKeyGuard as a provider.
tests/contracts/src/schemas.ts + contract.ts + streams.contract.ts ingestStreamEvent contract entry + request/response schemas.
api/src/contract-provider.spec.ts Provider-side contract execution incl. auth header handling + API-key env.
xstreamroll-sdk/src/types.ts + client.ts StreamConfig.apiKey + publishEvent() — sends the API key header, posts to /streams/events.
xstreamroll-sdk/__tests__/contract.consumer.test.ts + client.integration.test.ts Consumer contract test + request-shape integration test.
xstreamroll-sdk/README.md Documented apiKey config + publishEvent.
api/src/database.integration.spec.ts DB-level tests proving insertPendingEvent writes a row and dedupes on client_event_id.

Integration changes outside the module

  • api/src/contract-provider.spec.ts — added the API-key header injection + STREAM_API_KEY env so the contract provider can exercise the guarded endpoint.
  • api/src/streams/repository/streams.repository.ts (in-memory) — gained the two pending-event methods so the interface matches the DB repo; both are needed for the service-level tests.

Acceptance criteria coverage

  • Add POST /streams/events endpoint guarded by an API key (stream-api-key.guard.spec.ts — rejects missing/mismatched key, streams.controller.spec.ts — handler delegates)
  • Accept a stream event payload and enqueue it for the processing worker (streams-db.repository.ts insertPendingEvent, proven by database.integration.spec.ts)
  • Return 201 with the created event (streams.controller.spec.ts)
  • Deduplicate retried events via client_event_id (database.integration.spec.ts — unique constraint test)
  • SDK exposes publishEvent that sends the API key (contract.consumer.test.ts, client.integration.test.ts)
  • Contract covers both sides (tests/contracts/src/streams.contract.ts, provider + consumer suites pass)

Test plan

  • api: npm test — 322/325 passing (3 pre-existing failures: list-streams id serialization, streams.controller stale description expectation, jwt-secret-validator env-dependent — all reproduced on base)
  • xstreamroll-sdk: npm test — 66/67 passing (1 pre-existing getStreamStatus fixture failure, reproduced on base)
  • Typecheck — clean
  • Lint — 0 errors on all changed files (staged files pass the repo's --max-warnings=0 gate)
  • Contracts build — succeeds

Env vars / Notes

STREAM_API_KEY=             # required; gates POST /streams/events via X-Stream-Api-Key header

STREAM_API_KEY is required by config/env.ts in non-development environments; deployments must set it before the ingest endpoint can be used.

Adds an API-key-guarded ingest endpoint that queues stream events for the
processing worker via the existing pending-events repository, and wires a
matching publishEvent method into the SDK. Contract tests cover the request
shape in both the provider and consumer suites.

Closes XStreamRollz#514

@Xhristin3 Xhristin3 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Gracora please resolve conflicts

@Gracora

Gracora commented Aug 24, 2026

Copy link
Copy Markdown
Contributor Author

@Xhristin3 resolved.

@Xhristin3 Xhristin3 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@Xhristin3
Xhristin3 merged commit f3306ab into XStreamRollz:main Aug 24, 2026
2 of 11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SDK publishEvent() targets POST /streams/events, which the API does not expose: no ingestion path exists

2 participants