Greet a visitor
Serve a greeting over HTTP: a default hello at the root, and a personalised one at
/hello/:name. Names must be sensible, and invalid ones should be rejected at the
boundary rather than deep inside a handler.
commons hello.text
---Who we are greeting — non-empty, at most 40 characters.
A refined type: every `Subject` that exists has already passed thischeck, so `greeting` never needs to validate its input.---type Subject = String where NonEmpty and MaxLength(40)
---The canonical greeting for a subject.---fn greeting(subject: Subject) -> String { "Hello, \(subject)!"}context hello.web
uses hello.text
consumes bynk { Logger }
service api from http { on GET("/") by v: Visitor () -> Effect[HttpResult[String]] given Logger { let _ <- Logger.info("greeting the world") Ok(greeting("World")) }
on GET("/hello/:name") by v: Visitor (name: String) -> Effect[HttpResult[String]] given Logger { match Subject.of(name) { Ok(subject) => { let _ <- Logger.info("greeting \(subject)") Ok(greeting(subject)) } Err(_) => BadRequest("a name must be non-empty and at most 40 characters") } }}test hello.text { test "greets the world" { assert greeting("World") == "Hello, World!" }
test "greets any valid subject" { assert greeting(Mock[Subject]("Bynk")) == "Hello, Bynk!" }
test "rejects an empty subject" { assert Subject.of("") is Err(_) }}This example reaches Workers-only shapes (storage bindings, agents, or cron), so it runs with bynk dev rather than in the browser playground. See Install to get started.
How it works
Section titled “How it works”The language pieces live in commons hello.text. Subject is a refined String —
non-empty and at most 40 characters — and greeting takes a Subject, so it never
validates its argument: an invalid subject cannot be constructed, so it cannot reach
the function.
The service lives in context hello.web, which uses hello.text and declares
consumes bynk { Logger }. The context is the unit of deployment: it becomes one
Cloudflare Worker. Its service api from http block holds two handlers. Each is
declared by Visitor and given Logger, so the logging dependency is visible in
the signature, supplied by the platform, and mockable in tests rather than reached
for ambiently.
The handlers show where validation belongs. GET("/") greets "World" directly.
GET("/hello/:name") receives a raw String and runs Subject.of(name), matching
the Result: an Ok greets the validated subject, an Err returns BadRequest.
The handlers return HttpResult, and the compiler generates the router, the
boundary handling, and the Worker entry point around them. A test in
tests/hello/text.bynk fabricates a pinned Mock[Subject] and asserts the greeting
and the empty-subject rejection, with no harness code.