# hello-tiny-routes — a Worker composed out of routes

The same greeting as [`../hello-clack`](https://github.com/making/rontolisp/blob/develop/examples/cloudflare-workers/hello-clack), written the way
[tiny-routes](https://github.com/jeko2000/tiny-routes) wants it: the application
is not a function you write but one the library **composes** — a route table,
threaded through middleware.

```bash
./build.sh          # worker.lisp -> src/worker.wasm
npx wrangler dev    # http://localhost:8787
npx wrangler deploy
```

```console
$ curl http://localhost:8787/
Hello from tiny-routes on Cloudflare Workers!

$ curl -i http://localhost:8787/anything
HTTP/1.1 404 Not Found
Content-Type: text/plain; charset=utf-8

no route for /anything
```

## The whole program

```lisp
(ql:quickload '("clack" "clack-handler-reactor" "tiny-routes/lite"))

(tiny:define-routes *routes*
  (tiny:define-get "/" ()
    (tiny:ok (format nil "Hello from tiny-routes on Cloudflare Workers!~%")))
  (tiny:define-get "/hello/:name" (req)
    (tiny:ok (format nil "Hello, ~a!~%" (tiny:path-parameter req :name))))
  (tiny:define-any "*" (req)
    (tiny:not-found (format nil "no route for ~a~%" (tiny:path-info req)))))

(defparameter *app*
  (tiny:pipe *routes*
             (tiny:wrap-response-content-type "text/plain; charset=utf-8")))

(clack:clackup *app* :server :reactor :use-thread nil)
```

Four things make it tiny-routes rather than a `cond` in disguise:

- **A route is a handler, and `nil` DECLINES.** `define-routes` is "try these in
  order, take the first non-`nil`", which is why the last route, `"*"`, is the
  404 and needs no special mechanism.
- **A path template binds parameters.** `"/hello/:name"` matches one segment and
  `path-parameter` reads it; `/hello` and `/hello/` do not match it, so they
  decline into the 404 like anything else.
- **Response constructors name the status.** `ok` and `not-found` build the
  Clack triple instead of spelling the number.
- **`pipe` threads the table through middleware.** `wrap-response-content-type`
  sets the header for every route at once, so no route sets one — and
  [`../httpbin-tiny-routes`](https://github.com/making/rontolisp/blob/develop/examples/cloudflare-workers/httpbin-tiny-routes) uses the same seam to read
  the request body and parse the query string.

`tiny` is the library's own nickname, so there is no `defpackage` here either:
every name it contributes is reachable qualified, which keeps the library's
surface visible at each call site.

`*app*` is still an ordinary Clack application, so it runs on hunchentoot, on
woo, under `wasmtime serve` and on the JVM; the
[`../hello-clack` README](../hello-clack/README.md) explains the `:server`
designator half.

## `tiny-routes/lite`, and why it is on the `quickload` line

A tiny-routes path template compiles to a **cl-ppcre scanner at run time**, so
in a compiled module the whole regex engine is genuinely reachable and the
tree-shaker is right to keep it. `"tiny-routes/lite"` is the opt-in system: the
same source tree with the path-template matcher swapped for a ppcre-free one and
the `:cl-ppcre` dependency dropped with it. It accepts templates of literal
characters and `:name` tokens, matches them exactly as the full system does, and
**refuses at route-build time** on a regex metacharacter or a `:regex t`
template — which under `--no-wasi` means at `_initialize`, so it is a build-time
decision rather than a request-time surprise. Exact subset: the
[ASDF systems guide](https://github.com/making/rontolisp/blob/develop/examples/cloudflare-workers/doc/en/guides/asdf-systems.md); what it is worth
in bytes: the
[size report](https://github.com/making/rontolisp/blob/develop/examples/cloudflare-workers/size-report/results/cloudflare-workers.md).

## Developing without Cloudflare

As in [`../hello-clack`](../hello-clack/README.md#developing-without-cloudflare):
the synthesized export calls `clack.handler.reactor:dispatch`, an ordinary
function, so the whole Worker runs on every backend, which
[`examples/examples.yaml`](../../examples.yaml) pins:

```bash
rontolisp check.lisp
rontolisp check.lisp -o Check.class && java -cp rontolisp-0.1.0-SNAPSHOT-exec.jar:. Check
rontolisp check.lisp -o check.wasm --optimize && wasmtime run -W gc -W exceptions=y check.wasm
```

The first build downloads clack, lack and tiny-routes into
`~/.rontolisp/quicklisp`; after that everything is offline, because the
`ql:quickload` is resolved at **compile** time and inlined into the module.

## What's in here

| File | Purpose |
| --- | --- |
| [`worker.lisp`](worker.lisp) | The whole program. This is what `build.sh` compiles. |
| [`check.lisp`](check.lisp) | Drives it with no Cloudflare in sight, on any backend. |
| [`src/index.js`](src/index.js) | Three lines over the generated `worker()`. |
| [`src/worker.js`](src/worker.js) | The boundary, GENERATED by `--emit-js-glue` from `worker.lisp`'s declarations. Byte-identical to `../hello-clack/src/worker.js`, because the declarations are. Do not edit; `./build.sh` rewrites it. |
| `src/worker.wasm` | A build product — run `./build.sh` first. |

## Limitations

The Worker sandbox and `--no-wasi` limitations of
[`../hello-clack`](../hello-clack/README.md#limitations) apply unchanged.

## Rebuilding

```bash
./mvnw clean package -DskipTests   # from the repository root
examples/cloudflare-workers/hello-tiny-routes/build.sh
```
