Skip to content

Multiple backends

Use multiple backends when one Layeron project has separate app areas that are easier to maintain in separate entry files.

Terminal window
import { project } from "@layeron/core"
export default project({
backends: {
api: "./src/api.ts",
admin: "./src/admin.ts",
jobs: "./src/jobs.ts",
},
})

Each backend entry default-exports a backend(...) app:

Terminal window
import { backend } from "@layeron/core"
import { db, storage } from "@layeron/modules"
const database = db({ name: "main" })
const files = storage.bucket({ name: "uploads" })
const app = backend({
project: "billing",
name: "api",
compatibilityDate: "2026-05-24",
})
app.use(database)
app.use(files)
app.get("/api/health", () => ({ ok: true }))
export default app
Terminal window
import { backend } from "@layeron/core"
import { db } from "@layeron/modules"
const database = db({ name: "main" })
const app = backend({
project: "billing",
name: "admin",
compatibilityDate: "2026-05-24",
})
app.use(database)
app.get("/admin/health", () => ({ ok: true }))
export default app

All backend entries in one project({ backends }) compile into one AppSpec, RuntimeTopology, resource graph, deployment plan, and deployment state for the selected environment.

Layeron records the backend id in declaration metadata so compiled records, diagnostics, logs, and dashboard views can point back to the entry that declared them.

Backend entries with the same public runtime fields share one generated app Worker. Backend entries with different public runtime fields get separate app Workers automatically.

Public runtime fields are:

  • endpoint
  • gateway
  • placement
  • observability

Each generated app Worker receives the routes, custom domain, gateway policy, compute placement, and Worker observability settings for its backend group.

Equal module declarations share the same logical resource.

Terminal window
const database = db({ name: "main" })
const files = storage.bucket({ name: "uploads" })

If two backend entries declare the same module with the same configuration, Layeron lowers that module once. The backend entries can use the shared module from their own routes and handlers.

Use different namespace or name values when app areas need separate resources:

Terminal window
const adminDatabase = db({
namespace: "admin",
name: "main",
})

Every backend entry must use the same backend({ project }) slug. The project slug is the deployment and resource naming boundary.

These fields may differ per backend entry:

  • endpoint
  • gateway
  • placement
  • observability

Layeron stores per-backend values in AppSpec.metadata.backends[]. When every backend entry uses the same value, Layeron also keeps the shared value at the top level for compatibility with single-backend tooling.

These fields must match across backend entries when they are present:

  • compatibilityDate
  • compatibilityFlags

Use project({ env }) in layeron.config.ts for non-secret environment values that should apply to the whole compiled project.

Split backends when code ownership, routing, or deployment review is clearer with separate files:

  • public API routes
  • admin routes
  • queue or job handlers
  • webhook handlers
  • app-area-specific modules

Keep one backend entry when the app is small or the split would only move a few routes without making ownership clearer.