server

A small web server for local tools and dashboards. app = server.create(); app.get("/items/{id}", fn(req) { return server.json({...}) }); app.listen(8000). Handlers get a req map (method, path, params, query, headers, body, json, form, cookies, ip) and give back text, a map/list (sent as JSON) or a response from server.json/text/html/file/redirect. Safe defaults: listens on 127.0.0.1 only, bodies up to 10 MB, static files can't escape their folder, handler errors become a 500 JSON answer. app.ws(path, fn) serves WebSockets.

This library listens for web requests on your computer, so it needs the TB app. The examples here show the code; run them with tb file.tb.

import server

Example

Needs the TB app on your computer, so there is no Run button here.

import server

let app = server.create()
let notes = [{"id": 1, "text": "check auth.log", "by": "Tanisha"}]
app.get("/", fn(req) {
  return server.html("<h1>Notes</h1>")
})
app.get("/notes/{id}", fn(req) {
  return notes.filter(fn(n) {
    return str(n.id) == req.params.id
  })
})
app.post("/notes", fn(req) {
  notes.push({"id": len(notes) + 1, "text": req.json.text, "by": "Tanisha"})
  return server.json(notes.last(), 201)
})
app.listen(8000)   # http://127.0.0.1:8000, this computer only

Functions

json(data, status?, headers?)

returns response

A JSON answer (Content-Type application/json), status 200 unless given.

Visualizehow server.json works

server.json({"ok": true, "items": 3}, 200) gives {"_response": true, "status": 200, "body": "{\"ok\": true, \"items\": 3}", "headers": {"C…

server.json: inputs flowing into the function and the resultdataoktrueitems3status?200headers?left outserver.jsonresult · response200OKContent-Type: application/json; charset=utf…{"ok": true, "items": 3}dataoktrueitems3status?201headers?left outserver.jsonresult · response201CreatedContent-Type: application/json; charset=utf…{"ok": true, "items": 3}dataoktrueitems3status?404headers?left outserver.jsonresult · response404Not FoundContent-Type: application/json; charset=utf…{"ok": true, "items": 3}

Real result: worked out by running this example with TB while the site was built.

text(body, status?, headers?)

returns response

A plain-text answer.

Visualizehow server.text works

server.text("pong", 200) gives {"_response": true, "status": 200, "body": "pong", "headers": {"Content-Type": "text/plai…

server.text: inputs flowing into the function and the resultbody"pong"status?200headers?left outserver.textresult · response200OKContent-Type: text/plain; charset=utf-8pongbody"pong"status?202headers?left outserver.textresult · response202Content-Type: text/plain; charset=utf-8pongbody"pong"status?503headers?left outserver.textresult · response503Content-Type: text/plain; charset=utf-8pong

Real result: worked out by running this example with TB while the site was built.

html(body, status?, headers?)

returns response

An HTML page answer (the template library makes good bodies).

Visualizehow server.html works

server.html("<h1>Alerts</h1><p>3 new</p>") gives {"_response": true, "status": 200, "body": "<h1>Alerts</h1><p>3 new</p>", "headers": {"Co…

server.html: inputs flowing into the function and the resultbody<h1>Alerts</h1><p>3 new</p>status?left outheaders?left outserver.htmlresult · response200OKContent-Type: text/html; charset=utf-8<h1>Alerts</h1><p>3 new</p>

Real result: worked out by running this example with TB while the site was built.

file(path, options?)

returns response

Send a file from disk, with its type guessed from the name. A missing file gives a 404 answer.

Options

  • download a file name like "name.csv". It makes browsers save the file.
  • type the type to send, like "text/csv".
Visualizehow server.file works

server.file("data/users.csv", {"download": "users.csv"}) gives {"_response": true, "status": 200, "body": b"name,role\x0aAda,admin\x0aBo,user\x0a", "hea…

server.file: inputs flowing into the function and the resultpath"data/users.csv"options?download"users.csv"server.fileresult · response200OKContent-Type: text/csv; charset=utf-8Content-Disposition: attachment; filename="…name,roleAda,adminBo,user

Real result: worked out by running this example with TB while the site was built.

redirect(url, status?)

returns response

Send the browser somewhere else (status 302 unless given, 301 for permanent).

Visualizehow server.redirect works

server.redirect("/login", 302) gives {"_response": true, "status": 302, "body": b"", "headers": {"Location": "/login"}}

server.redirect: inputs flowing into the function and the resulturl"/login"status?302server.redirectresult · response302FoundLocation: /loginb""url"/login"status?301server.redirectresult · response301Moved PermanentlyLocation: /loginb""

Real result: worked out by running this example with TB while the site was built.

response(status, body?, headers?)

returns response

Any answer you like: server.response(201, "created", {"Location": "/items/7"}).

Visualizehow server.response works

server.response(201, "created", {"Location": "/items/7"}) gives {"_response": true, "status": 201, "body": "created", "headers": {"Location": "/items/7"}}

server.response: inputs flowing into the function and the resultstatus201body?"created"headers?Location"/items/7"server.responseresult · response201CreatedLocation: /items/7created

Real result: worked out by running this example with TB while the site was built.

error(status, message)

returns response

A JSON error answer: {"error": message, "status": status}.

Visualizehow server.error works

server.error(404, "no such note") gives {"_response": true, "status": 404, "body": "{\"error\": \"no such note\", \"status\": 404…

Drawing…

Real result: worked out by running this example with TB while the site was built.

create(options?)

returns app

Make a web app.

Options

  • log print one line per request (default: true).
  • max_body the biggest request body, in bytes (default: 10 MB).
  • cors true or an origin. It lets browser pages from elsewhere call the API.
  • name a name for the app (text).

The app has

  • get, post, put, patch, delete, route(path, fn) add a handler for a path.
  • static(prefix, folder) serve the files in a folder.
  • ws(path, fn) serve WebSockets.
  • listen(port?, host?) run the server until Ctrl+C.
  • start(port?, host?) run the server in the background. stop() stops it.
  • .port, .url, .running where it runs and whether it is on.

Example

let app = server.create()
app.get("/hello", fn(req) { return "Hi!" })
app.listen(8000)
Visualizehow server.create works

server.create({"log": true, "cors": true}) gives <app>

server.create: inputs flowing into the function and the resultoptions?logtruecorstruea browser asksGET /notesapp.get("/notes", fn)your function runsfn(req) { server.json(notes) }app.listen(8080)the answer goes back200 OK[{"id": 1, ...}]result · app<app>

Illustration: example values, drawn from what the function does.

stop_all()

Stop every running server made by this program.

Visualizehow server.stop_all works

server.stop_all()

server.stop_all: inputs flowing into the function and the resultserver :8080runningserver :9000runningserver.stop_all()all stoppedports are free again

No inputs: call it as it is.

Illustration: example values, drawn from what the function does.

escape(text)

returns text

Text made safe inside HTML (same as template.escape).

Visualizehow server.escape works

server.escape("<script>alert(1)</script>") gives "&lt;script&gt;alert(1)&lt;/script&gt;"

server.escape: inputs flowing into the function and the resulttext"<script>alert(1)</script>"server.escaperesult · text"&lt;script&gt;alert(1)&lt;/script&gt;"

Real result: worked out by running this example with TB while the site was built.

parse_query(text)

returns map

Read a query string: parse_query("a=1&b=x%20y") is {"a": "1", "b": "x y"}.

Visualizehow server.parse_query works

server.parse_query("q=ssh%20brute&page=2&debug") gives {"q": "ssh brute", "page": "2", "debug": ""}

server.parse_query: inputs flowing into the function and the resulttext"q=ssh%20brute&page=2&debug"server.parse_queryresult · mapq"ssh brute"page"2"debug""

Real result: worked out by running this example with TB while the site was built.

Values

NameValueWhat it is
server.max_body10485760

The default request-body limit in bytes (10 MB).

Visualizehow server.max_body works

server.max_body gives 10485760

The value server.max_bodyserver.max_body10485760what it isThe default request-body limit in bytes (10MB).

No inputs: call it as it is.

Real value, read from TB while the site was built.

server.methods["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]

The HTTP methods route() accepts.

Visualizehow server.methods works

server.methods gives ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]

The value server.methodsserver.methods"GET""POST""PUT""PATCH""DELETE""HEAD""OPTIONS"what it isThe HTTP methods route() accepts.

No inputs: call it as it is.

Real value, read from TB while the site was built.