KB-001 — "Getting API is not available" on the main page
KB-001 — “Getting API is not available” on the main page
Series: Bloqr Compiler Operations & Troubleshooting KB
Component: worker/worker.ts — Cloudflare Worker API entrypoint
Service URL: https://bloqr-backend.jk-com.workers.dev
Date Created: 2026-03-17
Status: Active
Symptom
The Angular SPA home page shows “Getting API is not available” on initial load at https://bloqr-backend.jk-com.workers.dev.
API fetches from the frontend to /api/version, /api/clerk-config, /api/turnstile-config, or /api/sentry-config may be returning non-200 responses, causing the Angular app to render the error state.
How the Home Page Gets API Availability
The Angular frontend bootstraps by calling a set of config/version endpoints. If any fail with an error status, a guard in the home component sets the “API not available” display state. The worker serves these at the following paths (all GET, public, no auth required):
| Endpoint | Purpose |
|---|---|
GET /api/version | Returns compiler version and latest deployment record from D1 |
GET /api/clerk-config | Returns Clerk publishable key (or null) |
GET /api/turnstile-config | Returns Turnstile site key and enabled flag |
GET /api/sentry-config | Returns Sentry DSN for frontend RUM |
All of these run before the ZTA auth chain (they are pre-auth handlers in _handleRequest). If they’re failing, the cause is at the infrastructure layer, not the auth layer.
Diagnostic Commands
Run these from any terminal. A healthy deployment returns 200 with a JSON body on all four.
# 1. Test root API info (bypasses HTML redirect)curl -s "https://bloqr-backend.jk-com.workers.dev/api?format=json" | jq .
# 2. Test the version endpoint (hits D1)curl -s "https://bloqr-backend.jk-com.workers.dev/api/version" | jq .
# 3. Test health (checks D1, KV, compiler binding, auth, gateway)curl -s "https://bloqr-backend.jk-com.workers.dev/health" | jq .
# 4. Test metrics (hits KV METRICS namespace)curl -s "https://bloqr-backend.jk-com.workers.dev/metrics" | jq .
# 5. Tail the live worker logwrangler tailRoot Cause Decision Tree
❶ Does GET /health return "status": "healthy"?
If NO — check the services object in the health response for the failing subsystem:
{ "status": "down", "services": { "database": { "status": "down" }, "cache": { "status": "healthy" }, "auth": { "status": "degraded", "provider": "none" }, "compiler": { "status": "degraded" }, "gateway": { "status": "healthy" } }}| Failing service | Likely cause | Fix |
|---|---|---|
database: down | D1 binding broken or migration pending | Re-deploy; run pending migrations |
auth: degraded, provider: none | Neither CLERK_JWKS_URL nor JWT_SECRET is set | wrangler secret put JWT_SECRET |
compiler: degraded | BLOQR_COMPILER_DO Durable Object binding missing | Deploy tail worker first; re-deploy |
cache: down | COMPILATION_CACHE KV namespace missing/unbound | Check KV namespace in Cloudflare dashboard |
❷ Does GET /api/version return 503?
This endpoint calls getLatestDeployment(env.DB). A 503 means env.DB is null — the DB D1 binding is missing from the deployed worker.
Check: wrangler d1 list — confirm bloqr-backend-app-db exists and the ID matches wrangler.toml.
wrangler d1 list# Should show: bloqr-backend-app-db 3e8e7dfe-3213-452a-a671-6c18e6e74ce5❸ Is CORS blocking the browser request?
The worker reads the CORS_ALLOWED_ORIGINS value at runtime. There are two sources for this value and they can conflict:
| Source | Value | Precedence |
|---|---|---|
wrangler.toml [vars] | "http://localhost:4200,...,https://bloqr-backend.jk-com.workers.dev" | Lower |
wrangler secret put CORS_ALLOWED_ORIGINS | Whatever was set when the command was last run | Higher — overrides [vars] |
If a secret was previously set with an outdated or incorrect value, it silently overrides the [vars] entry.
# Check which secrets existwrangler secret list
# If CORS_ALLOWED_ORIGINS is listed as a secret, re-set it:wrangler secret put CORS_ALLOWED_ORIGINS# Enter: http://localhost:4200,http://localhost:8787,https://bloqr-backend.jk-com.workers.devTo verify, open Chrome DevTools → Network tab, look for the OPTIONS preflight or the failing GET, and check the Access-Control-Allow-Origin response header.
❹ Is the bloqr-tail worker missing?
wrangler.toml declares:
[[tail_consumers]]service = "bloqr-tail"If bloqr-tail doesn’t exist in your Cloudflare account, wrangler deploy will fail silently or deploy a broken worker. This is the most common cause of a “everything looks fine in code” deployment that still doesn’t work.
# Deploy the tail worker first (from wrangler.tail.toml)wrangler deploy --config wrangler.tail.toml
# Then re-deploy the main workerwrangler deploy❺ Does the Angular build exist in the ASSETS binding?
The [assets] binding points to ./frontend/dist/bloqr-backend/browser. If the Angular build artifact is missing (e.g., the frontend CI step failed or was skipped), the worker deploys without static assets. The SPA shell won’t serve, and the Angular app will never initialize.
# Trigger a fresh build manuallysh scripts/build-worker.sh
# Or re-run the full deploy (which runs the build script)wrangler deployWorker Code Reference
The relevant request routing lives in _handleRequest in worker/worker.ts:
GET /api—handleInfo()— browser requests (Accept: text/html) redirect to/api-docs; API clients must sendAccept: application/jsonor append?format=jsonGET /api/version— queries D1 viagetLatestDeployment(env.DB); returns 503 ifenv.DBis nullGET /api/clerk-config— returnsCLERK_PUBLISHABLE_KEY(ornull); no auth, cached 1 hourGET /api/turnstile-config— returnsTURNSTILE_SITE_KEYand enabled flag; no auth, cached 1 hourGET /api/sentry-config— returns Sentry DSN for frontend RUM; no authGET /health— performs live probes of all bound services; returns granular per-service status
handleInfo() content negotiation detail:
const wantsHtml = Boolean(env.ASSETS) && accept.includes('text/html') && searchParams.get('format') !== 'json';if (wantsHtml) { return Response.redirect(new URL(API_DOCS_REDIRECT, request.url).toString(), 302);}Frontend API fetches must send Accept: application/json or omit the Accept header to avoid being redirected to /api-docs.
ZTA Security Note
All four config endpoints are intentionally pre-auth — they expose no secrets, only public keys and feature flags. The Turnstile site key, Clerk publishable key, and Sentry DSN are all public values by design. If these endpoints are returning auth errors (401, 403), something has changed in the auth chain that should not have touched these paths. File a separate bug.
Resolution Summary
| Symptom | Root Cause | Fix |
|---|---|---|
"API not available" on home page | D1 DB binding missing | wrangler deploy after confirming D1 namespace exists |
"API not available" on home page | CORS_ALLOWED_ORIGINS secret overrides [vars] with stale value | wrangler secret put CORS_ALLOWED_ORIGINS |
"API not available" on home page | bloqr-tail service not deployed | Deploy tail worker, then re-deploy main worker |
"API not available" on home page | Angular build missing from ASSETS | Run sh scripts/build-worker.sh and re-deploy |
GET /api/version returns 503 | DB D1 binding null | Verify D1 database ID in wrangler.toml matches dashboard |
GET /health shows auth: degraded | CLERK_JWKS_URL and JWT_SECRET both unset | Set JWT_SECRET secret or configure Clerk |
Related KB Articles
- KB-002 — Hyperdrive binding connected but
databaseservice reportsdown - KB-003 — Database Down After Deploy — Live Debugging Session (2026-03-25)
- KB-004 — Prisma WASM instantiation error on Cloudflare Workers
- KB-005 — Better Auth Cloudflare integration issues: Worker CPU timeout and silent rate limiting bypass
Feedback & Contribution
If you discovered a new failure mode while using this article, please open an issue tagged troubleshooting and documentation in jaypatrick/bloqr-backend with the details so it can be captured in a follow-up KB entry.