Skip to content

Deployment Environments

Deployment Environments

The Bloqr Compiler uses a three-environment model: local, dev, and production. Both the API Worker (wrangler.toml) and the Angular frontend Worker (frontend/wrangler.toml) follow the same model.


Overview

flowchart LR
    LOCAL["Local\n─────────────────\nwrangler dev :8787\nng serve :4200\nSecrets from .dev.vars\nNo Cloudflare deployment"]
    DEV["Dev\n─────────────────\nwrangler deploy --env dev\n*.workers.dev subdomain\nENVIRONMENT=development\nAngular DevTools enabled"]
    PROD["Production\n─────────────────\nwrangler deploy\nCustom domain via [[routes]]\nENVIRONMENT=production\nlogpush=true"]
    LOCAL -->|"ready to preview on workers.dev"| DEV
    DEV -->|"validated, ready to ship"| PROD
LocalDevProduction
Deploy command (API)deno task wrangler:devdeno task wrangler:deploy:devdeno task wrangler:deploy
Deploy command (frontend)pnpm --filter bloqr-frontend run startdeno task ui:deploy:ng:devsh scripts/deploy-frontend.sh
URLhttp://localhost:8787https://<worker-name>.workers.devcustom domain via [[routes]]
Angular builddev server (HMR)ng build --configuration developmentng build (production config)
Angular DevTools✅ (dev server always unoptimised)✅ (sourceMap, no optimisation)❌ (minified, tree-shaken)
logpushN/A (not deployed)falsetrue
ENVIRONMENT varset in .dev.vars"development""production"

Local Development

No Cloudflare deployment is involved. Both workers run locally.

Terminal window
# API Worker → http://localhost:8787
deno task wrangler:dev
# Angular dev server → http://localhost:4200 (hot module reload)
pnpm --filter bloqr-frontend run start
# Angular Workers preview → http://localhost:4200 (mirrors production SSR)
pnpm --filter bloqr-frontend run preview

Secrets and local URL overrides live in .dev.vars (gitignored — copy from .dev.vars.example).


Dev Environment

Deploys to the auto-generated *.workers.dev subdomain without touching the production custom domain. Use this environment to:

  • Preview changes live on Cloudflare infrastructure
  • Debug with Angular DevTools (source maps enabled, no minification)
  • Test before merging to production

Deploy the API Worker to dev

Terminal window
deno task wrangler:deploy:dev
# equivalent: deno run -A npm:wrangler deploy --env dev

Deploy the Angular frontend to dev

Terminal window
deno task ui:deploy:ng:dev
# equivalent: pnpm --filter bloqr-frontend run build:dev
# + pnpm --filter bloqr-frontend run deploy:dev

Or step by step:

Terminal window
# Build with development configuration (sourceMap=true, optimization=false)
pnpm --filter bloqr-frontend run build:dev # ng build --configuration development
# Deploy to *.workers.dev (--env dev skips the [[routes]] custom domain block)
pnpm --filter bloqr-frontend run deploy:dev # wrangler deploy --env dev

What [env.dev] does

# wrangler.toml (API Worker) — and frontend/wrangler.toml mirrors this
[env.dev]
logpush = false # disable Logpush for the dev subdomain
[env.dev.vars]
ENVIRONMENT = "development"
  • Routes are NOT inherited — the dev environment is accessible only via the *.workers.dev subdomain. The [[routes]] custom domain block in the top-level config is not applied.
  • Bindings ARE inherited — KV namespaces, D1, R2, Queues, and service bindings are all shared with production by default. Create separate dev resources and override them inside [env.dev] when isolation is needed.
  • Secrets are sharedwrangler secret put applies to all environments. To set an env-specific secret, use wrangler secret put <KEY> --env dev.

Production Environment

The top-level wrangler.toml / frontend/wrangler.toml IS the production configuration. No [env.production] block is needed.

Terminal window
# API Worker
deno task wrangler:deploy
# Angular frontend (preferred — builds, injects CF Analytics token, deploys)
sh scripts/deploy-frontend.sh

Production deploys to the custom domains declared in the [[routes]] block of each wrangler.toml. See URL Management for how to change these URLs.


URL Configuration

All public-facing URLs are managed as [vars] entries and kept in sync across both wrangler.toml files. Replace the placeholder values below with your real domains:

VariablePurposeExample value
URL_FRONTENDAngular frontend workerhttps://app.<your-domain>
URL_APIAPI / backend workerhttps://api.<your-domain>
URL_DOCSDocumentation sitehttps://docs.<your-domain>
URL_LANDINGMarketing landing pagehttps://<your-domain>
CANONICAL_DOMAINDomain used for crawl-protection noindex logic<your-domain>

CANONICAL_DOMAIN controls the X-Robots-Tag: noindex, nofollow header. Any request arriving at a hostname that is neither <CANONICAL_DOMAIN> nor a subdomain of it will receive the noindex header. This prevents *.workers.dev and other temporary hostnames from being indexed by search engines while a custom domain is active.

Important: CANONICAL_DOMAIN must be the full domain you use — not just the registrable root. For api.bloqr.dev, set CANONICAL_DOMAIN = "bloqr.dev" so that all *.bloqr.dev subdomains are treated as canonical. Setting it to dev would match every .dev TLD hostname, which is wrong.

To swap all URLs at once, run:

Terminal window
# Interactive — prompts for root domain and (optionally) canonical domain
deno task domain:swap
# Non-interactive examples
deno task domain:swap -- --domain bloqr.dev
deno task domain:swap -- --domain bloqr.dev --canonical bloqr.dev
# Preview changes without writing any files
deno task domain:swap -- --domain bloqr.dev --dry-run

The script updates both [vars] and [env.dev.vars] in each wrangler.toml, so both environments stay in sync after a single run.

For detailed URL change steps, see URL Management.

The [env.dev.vars] block intentionally repeats these values. The dev environment runs on *.workers.dev, but the app still references canonical production URLs (e.g. for OpenGraph meta tags and API base URLs). Override them if you also have a dev custom domain.


TOML Scoping Rules

Two Wrangler-specific TOML gotchas apply to both wrangler.toml files:

1. Array-of-tables must come AFTER all bare top-level keys

In TOML, once you open an array-of-tables header ([[routes]], [[rules]], etc.), every bare key = value that follows — until the next [table] or [[table]] header — is parsed as a field of that array entry, not as a top-level key. This means if main, no_bundle, compatibility_date, etc. appear after [[routes]], they become route fields and Wrangler rejects the config.

Correct structure:

name = "my-worker"
main = "worker.ts"
workers_dev = true
logpush = true
# ... all other bare top-level keys ...
[[routes]] # array-of-tables only AFTER all bare keys
pattern = "api.<your-domain>"
custom_domain = true
[[rules]] # same rule applies to [[rules]]
type = "ESModule"
globs = ["**/*.mjs"]
[vars] # standard tables can follow
ENVIRONMENT = "production"

2. Custom domain routes must be bare hostnames

For custom_domain = true route entries, Cloudflare only accepts a bare hostname — no paths or wildcards:

# ✅ Correct
[[routes]]
pattern = "api.<your-domain>"
custom_domain = true
# ❌ Wrong — path/wildcard not allowed with custom_domain = true
[[routes]]
pattern = "api.<your-domain>/*"
custom_domain = true

Further Reading