Skip to content

feat!: require projectId and move Client API under /projects/{projectId}#7

Closed
jeroenrinzema wants to merge 1 commit into
mainfrom
feat/project-in-url
Closed

feat!: require projectId and move Client API under /projects/{projectId}#7
jeroenrinzema wants to merge 1 commit into
mainfrom
feat/project-in-url

Conversation

@jeroenrinzema

Copy link
Copy Markdown
Contributor

Summary

The Lunogram Client API now scopes every authenticated endpoint to a project. The project UUID becomes a path segment:

/api/client/.../api/client/projects/{projectId}/...

Auth is unchanged (same API key / token); only the URL gains the project. This is a hard cut — no backwards compatibility.

Per the locked design decision, the project is a required client-construction parameter, supplied once and injected into every Client API path automatically. Callers must NOT pass it per call.

Breaking change

  • ClientProps gains a required projectId: string. Omitting it is a TypeScript type error.
  • The Lunogram convenience constructor signature changes from (apiKey, urlEndpoint?) to (apiKey, projectId, urlEndpoint?).
  • projectId is validated at construction: it must be non-empty and a valid UUID, otherwise a ValidationError is thrown.

Before / after

- const lunogram = new Lunogram('your-api-key')
+ const lunogram = new Lunogram('your-api-key', 'your-project-uuid')

- const client = new Client({ apiKey: 'your-api-key' })
+ const client = new Client({ apiKey: 'your-api-key', projectId: 'your-project-uuid' })

- const browser = new BrowserClient({ apiKey: 'your-api-key' })
+ const browser = new BrowserClient({ apiKey: 'your-api-key', projectId: 'your-project-uuid' })

Resource usage (client.user.*, client.organization.*) is otherwise unchanged.

Old → new paths

Old New
/api/client/users /api/client/projects/{projectId}/users
/api/client/users/events /api/client/projects/{projectId}/users/events
/api/client/users/scheduled /api/client/projects/{projectId}/users/scheduled
/api/client/organizations /api/client/projects/{projectId}/organizations
/api/client/organizations/users /api/client/projects/{projectId}/organizations/users
/api/client/organizations/events /api/client/projects/{projectId}/organizations/events
/api/client/organizations/scheduled /api/client/projects/{projectId}/organizations/scheduled

The platform contract also moves users/inbox*, users/devices, organizations/inbox*, push/vapid, and auth-methods/{id}/sessions under /projects/{projectId}/.... Those endpoints are not implemented in this SDK, so no per-resource changes were needed — but the prefix is injected centrally in HttpHandler, so any future resource is scoped automatically with no double-prefix.

Implementation

  • The /projects/{projectId} segment is injected in exactly one place — HttpHandler.#request in src/core/http.ts — where every request URL is built.

Files touched

  • src/types/common.ts — add required projectId to ClientProps.
  • src/core/http.ts — store, validate (non-empty + UUID), and inject the project segment.
  • src/utils/crypto.ts — add isUuid helper (uses the existing uuid dependency).
  • src/index.tsLunogram constructor takes projectId.
  • README.md — updated all examples + new "Project Scoping" section with the path table.
  • test/e2e.test.ts — require LUNOGRAM_PROJECT_ID, construct with projectId.
  • test/url.test.ts — new unit suite asserting the /client/projects/{projectId}/ prefix for every resource, no double-prefix, and projectId validation.
  • package.json — add offline test script (vitest run test/url.test.ts).

Validation

  • pnpm build
  • pnpm lint
  • pnpm test ✅ (10 passed)
  • pnpm test:e2e requires a live API + LUNOGRAM_API_KEY / LUNOGRAM_PROJECT_ID, not run here.

Rollout

Depends on the platform Client API change that introduces the /projects/{projectId} path. Merge once that ships.

The Lunogram Client API now scopes every authenticated endpoint to a
project: `/api/client/...` becomes `/api/client/projects/{projectId}/...`.

- Add required `projectId: string` to `ClientProps` (and as the new second
  positional argument of the `Lunogram` constructor).
- Inject `/projects/{projectId}` centrally in `HttpHandler` so every current
  and future resource path is scoped automatically.
- Validate `projectId` at construction: non-empty and a valid UUID, else
  throw `ValidationError`.
- Update README, examples, and the e2e test; add a url.test.ts unit suite
  asserting the new path prefix and validation behaviour.

BREAKING CHANGE: clients must now be constructed with a project UUID, and all
Client API requests target `/api/client/projects/{projectId}/...`.
@jeroenrinzema

Copy link
Copy Markdown
Contributor Author

Superseded by #8, which now targets main and already contains this branch's require projectId commit in its ancestry. Closing in favor of #8.

jeroenrinzema added a commit that referenced this pull request Jun 25, 2026
Migrate the SDK to spec-driven code generation with a layered design:

- Generated low-level layer (`src/gen/schema.ts`): all paths, operations and
  payload types are generated from the vendored OpenAPI spec via
  `openapi-typescript`. Committed, never hand-edited.
- Hand-written facade (`Lunogram` / `Client` / `BrowserClient` + resources):
  keeps the stable public API, camelCase ergonomics, auth, projectId UUID
  validation, browser support and the error hierarchy.

The transport is backed by `openapi-fetch` typed by the generated schema. The
`{projectID}` path param, `Bearer` auth header and camelCase->snake_case body
mapping are applied once in the transport/middleware, never per operation.

Spec is vendored at a pinned platform commit (no platform release needed); see
`spec/SOURCE.md`. CI gains a drift check (`pnpm generate` + `git diff`) and a
weekly `spec-sync` bump bot. Public API is unchanged from PR #7.
@jeroenrinzema jeroenrinzema deleted the feat/project-in-url branch June 25, 2026 15:51
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.

1 participant