Anatomy of a Bynk service
What is Bynk? shows the ideas in three-line fragments. This page shows a complete, runnable program in one piece — refined types, a context, a capability, a stateful agent, and an HTTP service, wired together — so you can judge the shape and the verbosity at a glance before committing to the tutorials. Read it once top to bottom; the walkthrough underneath names each part.
context analytics
-- A refined domain type: a page id that has already been validated, so nothing-- downstream ever has to re-check it.type Page = String where MinLength(1) and MaxLength(64)
-- A capability: the one outside-world effect this context needs. The interface-- is declared here; the implementation is a provider.capability Clock { fn now() -> Effect[Int]}
provides Clock = SystemClock { fn now() -> Effect[Int] { 0 }}
-- A keyed, stateful agent: one independent counter per page. Store fields are-- persisted between calls; a never-seen key starts from their zero values.agent Counter { key page: Page
store hits: Cell[Int] store lastSeen: Cell[Int]
on call bump(at: Int) -> Effect[Int] { let next = hits + 1 hits := next lastSeen := at next }}
-- A service: the HTTP entry point. It asks for the `Clock` capability with-- `given`, addresses the agent by key, and runs both as effects.service api from http { on GET("/hits/:page") by Visitor (page: Page) -> Effect[HttpResult[Int]] given Clock { let at <- Clock.now() let counter = Counter(page) let total <- counter.bump(at) Ok(total) }}That is the whole program. Compiling it with bynkc produces TypeScript you can
read and deploy to Cloudflare Workers.
What each part is
Section titled “What each part is”context analytics— a bounded context: the unit Bynk deploys. On theworkerstarget it becomes one Worker; the agent inside it becomes a Durable Object. See How a Bynk program is shaped.type Page = String where …— a refined type. The predicate is checked once, at the boundary, so everyPagethat exists downstream is already valid andbumpnever re-validates. This is the “make illegal states unrepresentable” idea in one line. See the type-system philosophy.capability Clock+provides … = SystemClock— an effect made explicit. A handler cannot read the clock unless it is grantedClock; the provider is the implementation that actually does. See Understand the capability model.agent Counter— a keyed, stateful entity. EachPageis its own counter with its own persistedstorefields;:=writes the next value, and the writes commit atomically when the handler returns. See The agent model.service api from http— the entry point. It declares its needs withgiven Clock, addresses the agent by key (Counter(page)), and sequences effects withlet x <- …. TheEffect[HttpResult[Int]]return type makes both the effect and the HTTP outcome part of the contract. See Handle an HTTP request.
How it fits together
Section titled “How it fits together”The route binds :page to a Page, validating it at the edge. The handler runs
Clock.now() for a timestamp, addresses the Counter agent for that page, and
calls bump — which increments the persisted hits and returns the new total. Pure
domain types, an explicit effect, persistent state, and a typed HTTP boundary —
each named in the language, not left to convention.
Ready to build one yourself? Start at Tutorial 1; the stateful-agent and HTTP pieces arrive in Tutorial 5.