DeploymentDeployment 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...

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

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.

# 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

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

Deploy the Angular frontend to dev

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:

# 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 shared — wrangler 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.

# 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:

# 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

  • Cloudflare Workers Architecture — two-worker split, bundled vs independent SSR modes, CI/CD deploy flow
  • URL Management — single source of truth for service URLs
  • Environment Configuration — two-track env system (shell vs Wrangler), .dev.vars, secrets management
  • Wrangler Environments docs