ReviewOS

stacks/bun-router

Proposal: absorb @stacksjs/router features so it can be deprecated

#874
Closed glennmichael123 opened this 8 days ago ·
8 days ago

@stacksjs/router (in stacksjs/stacks at storage/framework/core/router/) is a thin wrapper around @stacksjs/bun-router that adds Stacks-specific glue. With the recent bun-router additions — string action handlers (Actions/Foo), file-based routing, class handlers — most of @stacksjs/router is now a re-export. Only two things remain genuinely missing from bun-router. Filing this to propose absorbing them upstream so @stacksjs/router can be deprecated.

What @stacksjs/router still provides that bun-router doesn't

1. AsyncLocalStorage request context

A runWithRequest() wrapper around route handlers and a request() / getCurrentRequest() accessor that lets any code in the request lifecycle (services, helpers, model hooks) reach the active request without explicit threading.

// User code anywhere in the request lifecycle
import { request } from '@stacksjs/bun-router'

async function logActivity() {
  const req = request()
  console.log(`${req.method} ${req.url}`)
}

Today implemented as ~40 lines using node:async_hooks. The Router itself wires runWithRequest() automatically around handler invocation; users only ever touch request() / getCurrentRequest().

2. Named-route URL generation, Laravel-style

The chainable route.get(...).name('users.show') pattern already exists in the bun-router fluent API — the resolver is what's missing.

route.get('/users/{id}', handler).name('users.show')
url('users.show', { id: 42 })  // → 'https://app.example/users/42'

Today implemented as a small Map<name, path> plus a parameter substituter (~30 lines). Path placeholders like {id} get filled from the params object; leftover keys become query string.

Suggested API additions

// 1. Request context — Router wires runWithRequest internally
import { request, getCurrentRequest } from '@stacksjs/bun-router'

// 2. Named-route URLs
import { url } from '@stacksjs/bun-router'
router.get('/users/{id}', handler).name('users.show')
url('users.show', { id: 42 })

Why land these in bun-router

  • They're general-purpose, not Stacks-specific. Anyone building a Laravel-style app on Bun benefits.
  • They're small (~70 LOC combined) and have no new runtime dependencies (node:async_hooks is built-in).
  • They unblock a clean deprecation of the wrapper package, removing duplicate exports and the "which route do I import?" confusion that's tripped up users on fresh app installs (see stacksjs/stacks#1835).

What stays out (intentionally)

  • Routes-registry-as-object loader (Routes.ts map → file imports with auto-prefix). bun-router already ships file-based routing; pushing a second routing-config pattern upstream is opinionated and bigger to land. Can stay as a tiny app-side helper.
  • Action validations schema hook — overlaps with bun-router's existing validation paths; tightly coupled to the Stacks Action class. Stays in Stacks.
  • trackQuery — debug-page query tracking. Framework-specific. Stays in Stacks.

Cross-reference

Companion deprecation/removal tracker to be filed in stacksjs/stacks after this lands.

Sign in to comment on this issue.