Skip to content
LexerLangv0.1.0

Routing

$app.routes.add, patterns, parameters and responding through $res.

LexerLang
$app.routes.add({
    "/": func($req, $res) as void
        $doc = new html_document
        $doc.body.add(`<h2>Home</h2>`)
        $res.document($doc)
    end func

    "/product/:id": func($req, $res) as void
        $res.send("id: " + $req.params.id)
    end func

    "/old": "/pages/new.lx"           # file route
})

The type of the value decides what happens: a function is called, a string renders that .lx file. Both can appear in the same table.

If the route table is empty, the older behaviour applies: the single document added with $app.document.add($doc) is served at /. Once the table has entries, a path that matches nothing returns 404.

Patterns#

PatternMatches$req.params
//
/login/login, /login/
/product/:id/product/42{id: "42"}
/a/:x/b/:y/a/1/b/2{x: "1", y: "2"}
/files/*path/files/a/b/c{path: "a/b/c"}

* may only appear at the end of a pattern, and captures the whole remaining path.

The order they are tried does not depend on the order they are written: patterns with more literal segments are tried first, and those containing * last. When /product/new and /product/:id are written together, which one comes first makes no difference.

Defining the same path twice is an error.

Parameters#

LexerLang
$req.params.id            # error if absent
$req.param("id")          # null if absent

$res — the response#

CallEffect
$res.document($doc)serves a page: layout applied, scripts embedded, minify runs
$res.send($value)sets the body; an html_document is accepted too
$res.json($value)JSON body + application/json
$res.status($code)status code (100–599)
$res.redirect($url)302
$res.redirect($url, 301)permanent redirect (300–399)
$res.header($name, $value)response header
$res.cookie($name, $value, {...})cookie

document and send do the same work; the difference is intent. send sends a body (text, raw HTML, JSON); document serves a page. document accepts nothing but a document — raising an error is better than quietly returning a page with no layout.

If a handler finishes without calling send, document or redirect that is an error — it does not quietly return a blank page. A second send is an error too: HTTP has one response.

A newline in a redirect target is rejected; if it passed, a new header line could be injected into the response.

The context a handler runs in#

The page runs from the top on every request, and then the matching handler is called with that run's own context. This is why per-request modules, such as sessions, see the right instance:

LexerLang
$redis = new redis({host: "localhost", port: 6379}).connect()
session.redis = $redis
session.start()                    # every request, before the handlers

$app.routes.add({
    "/": func($req, $res) as void
        $res.send(session.get("user_id"))
    end func
})

Passing data to a partial#

An included file does not see the caller's variables. The way to pass a value is set_data, and the file sees it as $data:

LexerLang
"/about/:id": func($req, $res) as void
    $about = new html_document
    $about.set_data({id: $req.params.id})
    $about = $about.from_file(path.pwd + "/about.lx")
    $res.send($about.to_string())
end func
LexerLang
# about.lx
$doc.body.add(`<p>id: ` + $data.id + `</p>`)

Using $data when none was given is an error; it does not pass silently.

Next#

Document and layout.