A JSON API for a todo list: list, read, add, complete and delete. There's no framework and no router to set up. Each endpoint is an ordinary function with an attribute saying which requests it answers.
@serve(port := 8080) turns server into the server. When it's called - here by @main - its body runs first, and then it serves every route in its folder until the server stops.
The map every route shares can't be set up with an inject in that body. An injection only lasts until the end of its block, and the routes aren't called from inside server's body - it has already finished by the first request. Instead, @injectLocally on const todos injects the map into every call in the folder, so each route reads the same one as &todos. A real application would provide its database connection the same way. Nothing is passed around by hand.
@get, @post, @patch and @delete each take a path. A route takes no parameters - the request it answers is &request, which the server injects before each call. {id} in the path is &request.params.get("id"), the query string of /todos?done=false is &request.query, and addTodo reads the todo's title from &request.body. Every value is text, so the route parses what it needs: requestedTodo turns the {id} into a todo, or into the answer for one that isn't a number or isn't there.
Each route answers with a Response - a status, headers and a body. Response.json sends a #JSON {{{ #{ todo } }}} document as application/json, with a status of its own when it's given one, like addTodo's 201. Response.empty() is an empty 204, and Response.text(..., 404) a message with a status.
A 400 or a 404 is an answer like any other, so the route builds it. requestedTodo gives either the todo or that answer - Todo | Response - and each route that looks a todo up matches on which it got, so a missing one is the same 404 everywhere. An error a route ends with, one it passed up with !!, is what went wrong in the server instead: it answers 500, the error's message as the body.
Routes are plain functions, so a test calls them directly. Each test starts with inject todos := ... and inject request := Request{...}, which work here because the routes are called from inside the test's own block. The nearest injection wins, so each test gets a fresh, empty map and the request it wants - Request's fields all have defaults, so it gives only the parts that matter - and checks the Response it gets back. Neither test opens a socket.
@serve and the route attributes.&request, and answering with a Response.@injectLocally, and replacing it in tests with inject.#JSON macro.Request and calling them as functions.