elements
Blog Demos UI Docs Pricing
Elements Manual
  • Getting Started
  • CLI
  • Build
  • Browser
  • Packages
  • TypeScript
  • Elements HTML
  • Assets
  • RPC
  • Router
  • Session
  • Database
  • Migrations
  • LiveTable
  • Channel
  • Jobs
  • Email
  • Payments
  • JSON
  • Async
  • Style
  • UI
  • JSOC
  • Config
  • Tests
  • Logs
  • Deploy
  • Editors
  • Recipes
  • Licenses
  • Releases
    • 0.28.1
    • 0.28.0
Manual / Releases/ 0.28.0

0.28.0

elements man releases/v0-28-0

Hosts move to server.domain, deploy.domain and both redirects are removed, and getEnv() is renamed env(). A config that still has the old keys builds, but its domains are ignored: no certificates and no redirects on the next deploy. Make these changes before deploying.

Domains

Every host the app answers is now an entry in server.domain, with values from each environment's env file. Before:

config.jsoc
deploy: { domain: ["example.com", "www.example.com", "shop.example.com"], redirects: { "www.example.com": "example.com", }, env: { staging: { domain: "staging.example.com", }, }, }

After:

config.jsoc
server: { domain: { default: env!("DOMAIN"), "shop.example.com": env!("DOMAIN_SHOP"), }, },

With one host, server.domain is a string: domain: env!("DOMAIN").

Add the values to every env file. development.env is committed, and its names end in .localhost, which every browser and curl send to your machine with no hosts file:

config/env/development.env
DOMAIN=localhost DOMAIN_SHOP=shop.localhost
config/env/production.env
DOMAIN=example.com DOMAIN_SHOP=shop.example.com
config/env/staging.env
DOMAIN=staging.example.com DOMAIN_SHOP=shop.staging.example.com

A missing value fails the build, so a deploy with no DOMAIN in production.env fails before it goes live. Remove domain from deploy and from every deploy.env.<name>.

Routes Name Keys

A route's domain is now a key of server.domain, and matches that key's host in the running environment. A route that named a host keeps working when the host is the key, as above. A second route for the development host goes:

index.ts
// before app.route({ domain: "shop.example.com", path: "/", handler: shop }); app.route({ domain: "shop.localhost", path: "/", handler: shop }); // after app.route({ domain: "shop.example.com", path: "/", handler: shop });

Routes with no domain still answer every host. A domain that is not a key, such as "*.example.com", still matches as a hostname.

Redirects

deploy.redirects and deploy.env.<name>.redirects are removed. The www pair of every server.domain value is a 301 to the value with no config: delete a "www.example.com": "example.com" entry, and do not list www.example.com as a domain unless it should serve the app itself. A value that starts with www. redirects the bare name to it instead.

A redirect to another domain becomes a route:

config.jsoc
// before deploy: { redirects: { "old.example.com": "example.com" }, } // after server: { domain: { default: env!("DOMAIN"), "old.example.com": env!("DOMAIN_OLD"), }, },
index.ts
app.route({ domain: "old.example.com", path: "/*", handler: (req, res) => { res.redirect(`https://example.com${req.url}`, 301); }, });

Register it before the domainless routes.

Service Domains

deploy.services[].domains keeps plain hosts, not keys. Read them from the env files so each environment has its own:

config.jsoc
services: [ { name: "api", port: 8080, domains: [env!("DOMAIN_API")] }, ],

getEnv Is env

app/shared/stripe.ts
// before import { getEnv } from "@elements/app"; if (getEnv() === "production") { ... } // after import { env } from "@elements/app"; if (env() === "production") { ... }
Prev 0.28.1
Product
Manual Pricing Compare
Company
History Contact
Legal
Privacy Policy Acceptable Use Terms License Agreement
© Elements Dev Corporation 2026