58 lines
2.5 KiB
Markdown
58 lines
2.5 KiB
Markdown
# Route handler effectification
|
|
|
|
Practical reference for converting server route handlers in `packages/opencode` to a single `AppRuntime.runPromise(Effect.gen(...))` body.
|
|
|
|
## Goal
|
|
|
|
Route handlers should wrap their entire body in a single `AppRuntime.runPromise(Effect.gen(...))` call, yielding services from context rather than calling facades one-by-one.
|
|
|
|
This eliminates multiple `runPromise` round-trips and lets handlers compose naturally.
|
|
|
|
```ts
|
|
// Before - one facade call per service
|
|
;async (c) => {
|
|
await SessionRunState.assertNotBusy(id)
|
|
await Session.removeMessage({ sessionID: id, messageID })
|
|
return c.json(true)
|
|
}
|
|
|
|
// After - one Effect.gen, yield services from context
|
|
;async (c) => {
|
|
await AppRuntime.runPromise(
|
|
Effect.gen(function* () {
|
|
const state = yield* SessionRunState.Service
|
|
const session = yield* Session.Service
|
|
yield* state.assertNotBusy(id)
|
|
yield* session.removeMessage({ sessionID: id, messageID })
|
|
}),
|
|
)
|
|
return c.json(true)
|
|
}
|
|
```
|
|
|
|
## Rules
|
|
|
|
- Wrap the whole handler body in one `AppRuntime.runPromise(Effect.gen(...))` call when the handler is service-heavy.
|
|
- Yield services from context instead of calling async facades repeatedly.
|
|
- When independent service calls can run in parallel, use `Effect.all(..., { concurrency: "unbounded" })`.
|
|
- Prefer one composed Effect body over multiple separate `runPromise(...)` calls in the same handler.
|
|
|
|
## Current route files
|
|
|
|
Current instance route files live under `src/server/routes/instance/httpapi`.
|
|
Most handlers already yield stable services at route-layer construction and then
|
|
close over those services in endpoint implementations.
|
|
|
|
Files still worth tracking here:
|
|
|
|
- [ ] `handlers/session.ts` — still the heaviest mixed file; some paths keep compatibility translations and direct event publication
|
|
- [ ] `handlers/experimental.ts` — mixed state; some handlers still rely on request-local context reads
|
|
- [ ] `middleware/*` — still contains compatibility policy for auth, compression, errors, instance context, and workspace routing
|
|
- [ ] `public.ts` — still owns SDK/OpenAPI compatibility translation shims
|
|
- [ ] raw route modules — WebSocket and catch-all routes should stay explicit and avoid rebuilding stable layers per request
|
|
|
|
## Notes
|
|
|
|
- Route conversion is now less about backend migration and more about removing the remaining direct `Instance.*` reads, request-local service plumbing, and OpenAPI compatibility shims.
|
|
- Prefer route-layer service capture over rebuilding or providing stable layers inside individual handlers.
|