feat(backend): wire feed renderers into API - #76
Conversation
Add FeedRenderer and FeedRendererProvider to support server-side rendering of feed items via ?render=json-render query param. - FeedRenderer maps sourceId to FeedItemRenderer, renders matching items and drops the rest - FeedRendererProvider mirrors FeedSourceProvider pattern for per-user renderer construction - UserSession exposes renderer, handler converts JrxNode to Spec - Returns 400 for unknown render format, 500 if renderer missing Co-authored-by: Ona <no-reply@ona.com>
| const renderedItems = session.renderer.render(feed.items).map((item) => ({ | ||
| ...item, | ||
| ui: render(item.ui), | ||
| })) |
There was a problem hiding this comment.
Both FeedItemRenderer functions and render() from @nym.sh/jrx can throw (e.g. malformed JRX node, duplicate keys, fragment at root). A single bad item would crash the entire request with an unhandled error.
Consider wrapping this in a try/catch to return a structured error, similar to how the enhancement pipeline handles failures gracefully:
| })) | |
| let renderedItems | |
| try { | |
| renderedItems = session.renderer.render(feed.items).map((item) => ({ | |
| ...item, | |
| ui: render(item.ui), | |
| })) | |
| } catch (err) { | |
| const message = err instanceof Error ? err.message : "Unknown rendering error" | |
| return c.json({ error: `Rendering failed: ${message}` }, 500) | |
| } |
|
Reviewed this PR and found 1 area that needs attention — see inline comment on the rendering error handling in Overall the implementation is well-structured: clean separation between |
What
Wire the
FeedItemRendererfunctions exported by source packages into the backend and expose them through the/api/feedendpoint via a?render=json-renderquery parameter.Changes
New types:
FeedRenderer— holds asourceId → FeedItemRenderermap.render(items)returnsRenderedFeedItem[], silently dropping items with no matching renderer.FeedRendererProvider— interface withfeedRendererForUser(userId): FeedRenderer, mirrorsFeedSourceProvider.Backend wiring:
tfl/renderer-provider.tsandcaldav/renderer-provider.tsexport source IDs and renderer functions.UserSessionaccepts aFeedRenderervia options and exposes it asreadonly renderer.UserSessionManageraccepts aFeedRendererProviderand passes per-userFeedRendererinstances to sessions.server.tsaggregates renderer entries and wires the provider.API behavior:
GET /api/feed— unchanged.GET /api/feed?render=json-render— renders items viaFeedRenderer, convertsJrxNode→Spec(json-render format) in the handler, returns items withuifield.rendervalue → 400.render=json-renderwith no renderer configured → 500.Other:
UserSessionconstructor now takes a single options object ({ sources, enhancer?, renderer? }).@nym.sh/jrxadded as backend dependency.