Incremental Adoption
Stratal is built on top of Hono and exposes its underlying HonoApp (which extends OpenAPIHono) via the Stratal.hono getter. This means you can mount a Stratal instance as a sub-app inside an existing Hono application, adopting it incrementally without rewriting everything at once.
Mounting Stratal into an existing Hono app
Section titled “Mounting Stratal into an existing Hono app”If you already have a Hono application running on Cloudflare Workers, you can mount Stratal under a route prefix using Hono’s app.route() method.
First, create a Stratal instance with your module:
import { Stratal } from 'stratal'import { UsersModule } from './users/users.module'
export const stratal = new Stratal({ module: UsersModule })Then mount it into your existing Hono app:
import { Hono } from 'hono'import { stratal } from './stratal-app'
const app = new Hono()
// Your existing routesapp.get('/health', (c) => c.json({ status: 'ok' }))app.get('/legacy/reports', (c) => { // existing handler logic return c.json({ reports: [] })})
// Mount Stratal under /apiconst stratalHono = await stratal.honoapp.route('/api', stratalHono)
export default { fetch: app.fetch, async queue(batch: MessageBatch) { await stratal.queue(batch) }, async scheduled(controller: ScheduledController) { await stratal.scheduled(controller) },}All routes defined in your Stratal modules will now be available under the /api prefix. For example, if UsersModule defines a GET /users route, it becomes GET /api/users.
Wiring up queues and cron jobs
Section titled “Wiring up queues and cron jobs”When you mount Stratal as a sub-app with app.route(), only HTTP routing is connected. Queue consumers and scheduled (cron) handlers are not part of the Hono routing layer — they must be explicitly forwarded from your worker’s export.
The code example above shows the full pattern: export an object with fetch, queue, and scheduled handlers instead of exporting app directly.
export default { fetch: app.fetch, async queue(batch: MessageBatch) { await stratal.queue(batch) }, async scheduled(controller: ScheduledController) { await stratal.scheduled(controller) },}If you skip the queue or scheduled exports, any queue consumers or cron jobs defined in your Stratal modules will silently do nothing.
Gradual migration
Section titled “Gradual migration”A practical migration strategy is to move one feature at a time into Stratal modules while keeping everything else in your existing Hono app:
- Pick a feature — Start with a self-contained feature like user management or billing.
- Create a Stratal module — Move the routes, services, and logic into a Stratal module with controllers and providers.
- Mount under a prefix — Use
app.route()to mount Stratal at the same path your existing routes used. - Remove old routes — Delete the original Hono handlers for the migrated feature.
- Repeat — Continue moving features until your entire app runs on Stratal.
import { Hono } from 'hono'import { stratal } from './stratal-app'
const app = new Hono()
// Migrated to Stratalconst stratalHono = await stratal.honoapp.route('/api/v2', stratalHono)
// Legacy routes still served by plain Honoapp.get('/api/v1/reports', (c) => c.json({ reports: [] }))app.post('/api/v1/webhooks', (c) => c.json({ received: true }))
export default { fetch: app.fetch, async queue(batch: MessageBatch) { await stratal.queue(batch) }, async scheduled(controller: ScheduledController) { await stratal.scheduled(controller) },}Over time, you can shrink the legacy section and expand the Stratal-managed routes.
Caveats and considerations
Section titled “Caveats and considerations”Async initialization
Section titled “Async initialization”The stratal.hono getter returns a Promise<HonoApp> because Stratal bootstraps asynchronously — it resolves modules, builds the DI container, and registers routes during initialization. You must await it before mounting.
DI container scope
Section titled “DI container scope”Stratal manages its own dependency injection container. Services registered in Stratal’s DI container are not available to your plain Hono handlers, and vice versa. If you need shared state between the two layers, consider using Cloudflare bindings (KV, D1, etc.) or migrating the shared logic into a Stratal provider.
Request-scoped containers
Section titled “Request-scoped containers”Each request that hits a Stratal route gets its own request-scoped DI container. This container is created automatically by Stratal’s internal middleware and disposed of after the response is sent. Requests handled by your plain Hono routes bypass this entirely.
Middleware ordering
Section titled “Middleware ordering”Middleware registered in your outer Hono app runs before Stratal’s middleware chain. If you have global middleware (e.g., authentication or logging) in your Hono app, it will execute first, followed by Stratal’s own middleware pipeline:
Request → Outer Hono middleware → Stratal request-scoped container setup → Stratal user-defined middleware → Stratal guards → Stratal route handler → ResponseKeep this in mind when migrating authentication or other cross-cutting concerns — you may need to configure them in both layers during the transition.
Error handling boundary
Section titled “Error handling boundary”Stratal catches all errors via its own onError handler and returns JSON error responses. Errors thrown within Stratal routes do not bubble up to the outer Hono app’s error handler.
During migration, this means error response formatting may differ between Stratal-managed routes and legacy Hono routes. If consistent error formatting is important, align your outer Hono app’s error handler to return the same JSON structure, or migrate error-sensitive routes together.
OpenAPI documentation
Section titled “OpenAPI documentation”Stratal’s automatic OpenAPI documentation only covers routes registered through Stratal modules. Your legacy Hono routes won’t appear in the generated spec. As you migrate routes into Stratal, they’ll automatically be included in the OpenAPI documentation.
Next steps
Section titled “Next steps”- Modules for organizing your application into feature modules.
- Controllers and Routing for defining routes in Stratal.
- Queues for wiring up queue consumers in Stratal.
- Cron Jobs for scheduling recurring tasks.
- Middleware for configuring middleware within Stratal modules.
- Dependency Injection for understanding the DI container lifecycle.