Built with Netjes
The screens are React, but the game runs in C#. Netjes puts React pages on ASP.NET Core: every request lands in C#, which loads the data, checks every bet and keeps the answers to itself until your position is settled. React only draws what C# sends it.
01 · React
Browser
Hydrates the page, then navigates on the client: the next page arrives as JSON, not HTML.
02 · Proxy
Apache
Passes fact-market.nl on to the app and terminates TLS.
03 · Netjes
ASP.NET Core
Routes app/trade/page.tsx, runs the C# loader, settles bets in server actions and stores them in SQLite.
04 · Renderer
Node
Started and supervised by .NET. Renders the React page to HTML for the first load, nothing more.
// Features/Trade/TradePage.cs
app.MapPageLoader("/trade", async (HttpContext http,
PlayerSession session, GameService game, CancellationToken ct) =>
Page.Props(await game.GetTradeStateAsync(
await session.FindAsync(http, ct), ct)));
app.MapAction("trade.place", async ([FromForm] PlaceInput input,
HttpContext http, PlayerSession session, GameService game,
CancellationToken ct) =>
{
var player = await session.GetOrCreateAsync(http, ct);
var result = await game.PlaceAsync(player, input.QuestionId!,
input.Probability, input.Stake!.Value, ct);
...
});// app/trade/page.tsx: props typed by the C# TradeState record
export default definePage(pages['/trade'], function Trade(props) { ... });
// components/TradeTicket.tsx: a form posting to the C# action
const [state, formAction, pending] =
useAction(serverActions['trade.place']);
<Form action={formAction}>…<button>Place bet</button></Form>See it in your browser's network tab: opening a page fetches its props as JSON from the C# loader (header X-Netjes-Data: 1), and every bet is a POST to /_netjes/actions/trade.place. With JavaScript off, the ticket still works as a plain form.