Shorten a URL
Accept a URL, store it under a short random code, and resolve that code back to the URL with an HTTP redirect. Codes should expire on their own, and malformed input should be rejected before any handler runs.
commons codes
---A short code: 6–12 url-safe characters. Every `Slug` that exists has alreadypassed this check, so it can be used as a key without re-validation. A refined`String`, so it projects structurally across the context boundary.---type Slug = String where MinLength(6) and MaxLength(12)
---A target URL — non-empty and bounded so a malformed body is rejected at theboundary, before any handler runs.---type Url = String where MinLength(1) and MaxLength(2048)
---The KV key for a slug. Namespacing keeps the link table separate from anythingelse stored in the same namespace.---fn keyOf(code: Slug) -> String { "link:\(code)"}context links
uses codesconsumes bynk { Random }consumes bynk.cloudflare { Kv } -- locks this unit to Cloudflare
type CreateRequest = { target: Url,}
type CreatedView = { code: Slug, target: Url,}
service api from http { -- Create a short link. A random uuid is sliced to a slug; the mapping is -- stored for a day (putTtl), after which it expires on its own. on POST("/links") by Visitor (body: CreateRequest) -> Effect[HttpResult[CreatedView]] given Random, Kv { let id <- Random.uuid() let raw = "\(id)" match Slug.of(raw.slice(0, 8)) { Err(_) => ServerError("could not generate a slug") Ok(code) => { let _ <- Kv.putTtl(keyOf(code), body.target, 86400) Created(CreatedView { code: code, target: body.target }) } } }
-- Resolve a short link: a `302 Found` redirect to the stored target, or a -- `404` if the slug is unknown or has expired. The redirect carries no body — -- the target URL travels in the `Location` header. on GET("/links/:code") by Visitor (code: Slug) -> Effect[HttpResult[Url]] given Kv { let stored <- Kv.get(keyOf(code)) match stored { Some(target) => Found(target) None => NotFound } }}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 boundary types live in commons codes. Slug is a refined String of 6–12
url-safe characters, and Url is a non-empty String bounded at 2048 characters,
so an invalid code can never be stored and an over-long URL is rejected with 400
before a handler sees it. The keyOf helper namespaces a slug into its KV key —
"link:\(code)" — keeping the link table distinct from anything else in the same
namespace.
The service lives in context links, which consumes bynk { Random } and
consumes bynk.cloudflare { Kv }. That second line locks the unit to Cloudflare: KV
is a platform-specific capability, declared in the signature and injected by the
toolchain rather than constructed in the code.
POST("/links") mints a code by calling Random.uuid(), slicing the result to
eight characters, and passing it through Slug.of. On success it stores the mapping
with Kv.putTtl(keyOf(code), body.target, 86400) — a one-day TTL, so the entry
disappears on its own with no sweep to write — and returns Created with the code
and target. GET("/links/:code") takes a Slug directly, reads
Kv.get(keyOf(code)), and matches the Option: Some(target) resolves to a
Found redirect carrying the URL in the Location header with no body, and None
becomes a 404.