# Local developer testing

Run the Worker on your machine, publish with an API key, and list the inbox with a user Cognito token. No Supabase and no Cognito M2M required for the happy path.

Order: boot (§1) → API-key publish + user list (§2) → optional Cognito M2M (§3).

When you’re ready to deploy to Cloudflare: Setup guide.

Prerequisites: Node ≥ 20, pnpm ≥ 9, and a Cognito user pool you can sign into (for inbox reads). Publisher API keys are configured only on the Worker.


# 1. Boot the Worker

git clone <this repo> && cd org.inovus.notifications
pnpm install
pnpm test                                             # everything should be green
cd packages/api
cp .dev.vars.example .dev.vars
pnpm dev                                              # wrangler dev on http://localhost:8787

Open http://localhost:8787/docs — interactive Swagger UI (Try it out uses this host). Confirm:

curl.exe http://localhost:8787/health
# → {"status":"ok"}

Default store is memory (wiped on restart). You do not need Supabase. Comparison: Storage backends.

Useful loops:

pnpm test
pnpm --filter @inovus-medical/notifications-api exec vitest run test/routes.test.ts
pnpm test:coverage
pnpm lint && pnpm typecheck
pnpm openapi:generate    # after changing routes/schemas

# 2. Send and receive with API keys (start here)

Publish without Cognito resource servers or M2M app clients. You still need AUTH_ISSUER so a signed-in user can read the inbox (API keys are publish-only).

More detail: api-keys.md.

# 2.1 Configure .dev.vars

Edit packages/api/.dev.vars (never commit this file):

AUTH_ISSUER=https://cognito-idp.<region>.amazonaws.com/<userPoolId>
DB_DRIVER=memory
PUBLISHER_API_KEYS=local:<long-random-secret>
# Leave PUBLISHER_SCOPES unset — not required for API-key publish

Generate a long random secret (password manager or openssl rand -hex 32). Restart pnpm dev so Wrangler reloads the vars.

Var Why
AUTH_ISSUER User access tokens from your pool verify (needed for list)
PUBLISHER_API_KEYS Backends publish with X-Api-Key
DB_DRIVER=memory No Supabase; data vanishes on Worker restart

# 2.2 Get a user access token (for receiving only)

Sign in as a normal user on that pool. Copy the access token and decode it at jwt.io:

$ApiKey = "<long-random-secret>"   # secret half of PUBLISHER_API_KEYS (after the colon)
$UserToken = "eyJraWQiOiJ...."      # paste user access token
$UserSub = "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"  # JWT "sub" claim

$UserSub is who you publish to — it must match that user’s sub or their inbox stays empty.

# 2.3 Publish with X-Api-Key

curl.exe -s -X POST http://localhost:8787/v2/notifications `
  -H "X-Api-Key: $ApiKey" `
  -H "Content-Type: application/json" `
  -d "{`"userGuid`":`"$UserSub`",`"title`":`"Hello`",`"body`":`"Local API key test`",`"severity`":`"success`",`"dedupeKey`":`"local-setup-1`"}"

Expect 201 and { "notification": { …, "source": "local", … }, "duplicate": false }.
Same command again → 200 with "duplicate": true.

Status Meaning
401 Wrong API key, or .dev.vars not reloaded (restart pnpm dev)
400 Sent both X-Api-Key and Authorization: Bearer

Do not use the API key on GET /v2/notifications403 (publish-only).

# 2.4 List the inbox with the user token

curl.exe -s http://localhost:8787/v2/notifications `
  -H "Authorization: Bearer $UserToken"

Expect your notification in items and unreadCount ≥ 1. Optional:

curl.exe -s http://localhost:8787/v2/notifications/unread-count `
  -H "Authorization: Bearer $UserToken"

curl.exe -s -X POST "http://localhost:8787/v2/notifications/$NotificationId/read" `
  -H "Authorization: Bearer $UserToken"

# 2.5 Same flow in Swagger

  1. Open http://localhost:8787/docs
  2. Publish: set X-Api-Key (API key auth)
  3. List: authorize with Bearer <user token>
  4. POST /v2/notifications then GET /v2/notifications

# 3. Optional — publish with Cognito M2M tokens

Prefer short-lived Cognito tokens instead of (or as well as) Worker API keys. Why/how: cognito-explained.md, cognito-setup.md, Cognito M2M reference.

# 3.1 Extra .dev.vars

Keep AUTH_ISSUER and DB_DRIVER=memory. Add:

PUBLISHER_SCOPES=notifications/publish
# You can keep PUBLISHER_API_KEYS — both paths work together

Create a Cognito resource server (notifications / publish) and a confidential app client with client credentials + that scope (cognito-setup.md). Restart pnpm dev.

# 3.2 Fetch an M2M access token

Variable Cognito console
$ClientId App integration → App clients → confidential client → Client ID
$ClientSecret Same page → Client secret
Cognito domain App integration → Domain
$ClientId = "paste-from-app-client-detail"
$ClientSecret = "paste-from-app-client-secret"
$CognitoDomain = "https://<prefix>.auth.<region>.amazoncognito.com"

curl.exe -X POST "$CognitoDomain/oauth2/token" `
  -H "Content-Type: application/x-www-form-urlencoded" `
  -u "${ClientId}:${ClientSecret}" `
  -d "grant_type=client_credentials&scope=notifications/publish"

$M2MToken = "eyJraWQiOiJ...."   # paste access_token

Reuse $UserSub / $UserToken from §2.

# 3.3 Publish with Bearer

curl.exe -s -X POST http://localhost:8787/v2/notifications `
  -H "Authorization: Bearer $M2MToken" `
  -H "Content-Type: application/json" `
  -d "{`"userGuid`":`"$UserSub`",`"title`":`"Hello`",`"body`":`"Local M2M test`",`"severity`":`"success`",`"dedupeKey`":`"local-m2m-1`"}"
Status Meaning
401 Wrong/expired token, or AUTH_ISSUER ≠ token iss
403 Client missing notifications/publish, or PUBLISHER_SCOPES mismatch

List with $UserToken as in §2.4.


# 4. Local troubleshooting

Symptom Likely cause
Publish 401 with X-Api-Key Secret not in PUBLISHER_API_KEYS, or Worker not restarted
Publish 400 Both X-Api-Key and Bearer sent
Publish 403 on list/inbox API keys are publish-only — use a user token
Everything 401 on Bearer AUTH_ISSUER ≠ token iss
Publish works but list empty userGuid ≠ user’s sub
Data disappeared Memory store — restart wiped it
Swagger hits production Use / server (“This deployment”); refresh after pulling latest

Optional: point local at a real Supabase project with DB_DRIVER=supabase + URL + service role in .dev.vars (never commit secrets).


# Next: deploy to Cloudflare

Setup guide — Supabase, Cognito (production), Worker deploy, GitHub Actions.