Skip to content

4. Make it live

We keep saying the catalog “stays live.” This chapter makes that concrete — and shows that you already built it, without writing a line of real-time plumbing.

Go back to every query we’ve written: each returns …Observe(…), not a one-shot list. That ISubject<…> is a stream. On the C# side, Arc serves it as an observable query; on the React side, the generated .use() hook subscribes to it. So the data flow isn’t “fetch once and render” — it’s a standing subscription:

writes

change stream

AddBook

Books

BooksForAuthor.use()

React re-renders

When a command writes, the database’s change stream notices, the observable query pushes the new value to every subscribed React component, and it re-renders. Open the app in two browser tabs, add a book in one, and watch it appear in the other — you wrote none of that plumbing. It falls out of returning Observe() and calling .use().

Nothing special — just the return type. A one-shot query returns the data:

public static IEnumerable<Author> AllAuthors(IMongoCollection<Author> authors) =>
authors.Find(_ => true).ToList();

Make it return an ISubject<…> via Observe() instead, and the exact same method becomes a live subscription — the proxy and the React .use() adapt automatically:

public static ISubject<IEnumerable<Author>> AllAuthors(IMongoCollection<Author> authors) =>
authors.Observe();

That’s the whole switch between “load once” and “stay live.” You choose per query: a rarely-changing lookup can be a plain list; a screen the librarian watches should observe.

  • A clear picture of why your screens update themselves — observable queries are standing subscriptions, database to browser.
  • The one-line difference between a one-shot query and a live one — the return type, nothing else.

The catalog is live. There’s just one thing left before it’s a real back office: right now anyone can register authors and add books. Let’s decide who’s allowed. Lock it down →