bserver can execute server-side scripts in Python, JavaScript (Node.js), or PHP to dynamically generate HTML content. This is used for rendering that requires logic beyond what static YAML can express.
Scripts are defined in format definitions using the script: field:
^my-renderer:
script: python
code: |
print(f'<p>{record["key"]}: {record["value"]}</p>')
When bserver encounters content with this format, it:
^renderer:
script: python
code: |
name = record.get('key', '')
value = record.get('value', '')
print(f'<div class="item">{name}: {value}</div>')
Available variable: record (a Python dict)
bserver looks for python3 first, then python.
^renderer:
script: javascript
code: |
console.log(`<div class="item">${record.key}: ${record.value}</div>`);
Available variable: record (a JavaScript object)
Aliases: javascript, js, node
^renderer:
script: php
code: |
echo "<div class='item'>{$record['key']}: {$record['value']}</div>\n";
Available variable: $record (a PHP associative array)
When the content is a map (ordered key-value pairs), each entry becomes a
record with key and value fields:
navlinks:
"/": Home
"/about": About
"/contact": Contact
The script receives via stdin:
[
{"key": "/", "value": "Home"},
{"key": "/about", "value": "About"},
{"key": "/contact", "value": "Contact"}
]
When content is a list of maps, each map becomes a record directly:
products:
- name: Widget
price: "$9.99"
sku: WDG-001
- name: Gadget
price: "$19.99"
sku: GDG-001
The script receives:
[
{"name": "Widget", "price": "$9.99", "sku": "WDG-001"},
{"name": "Gadget", "price": "$19.99", "sku": "GDG-001"}
]
Scripts can run even without content data. If the format references a name
that has no definition, the script receives null (wrapped as [null]).
This is useful for scripts that generate content entirely from environment
variables or external sources.
Instead of inline code, you can reference an external file:
^renderer:
script: php
file: scripts/render.php
The file path is relative to the document root. For PHP files, <?php and
?> tags are automatically stripped since bserver provides the execution
wrapper.
Scripts have access to CGI-like environment variables, making them behave similarly to scripts running under Apache or nginx:
| Variable | Description |
|---|---|
REQUEST_URI |
URL path for the current request (e.g., /about) |
DOCUMENT_ROOT |
Filesystem path to the document root |
REDIRECT_STATUS |
Always 200 |
SCRIPT_NAME |
Same as REQUEST_URI |
PHP_SELF |
Same as REQUEST_URI (for PHP compatibility) |
| Variable | Description |
|---|---|
REMOTE_ADDR |
Client IP address |
SERVER_NAME |
Server hostname |
SERVER_ADDR |
Server IP address |
SERVER_PORT |
Server port (80 or 443) |
HTTP_HOST |
HTTP Host header |
QUERY_STRING |
URL query string |
REQUEST_METHOD |
HTTP method (GET, POST, etc.) |
SERVER_PROTOCOL |
Protocol version (e.g., HTTP/1.1) |
GATEWAY_INTERFACE |
Always CGI/1.1 |
SERVER_SOFTWARE |
Always bserver |
SCRIPT_FILENAME |
Path to script file (if using file:) |
CONTENT_TYPE |
Request Content-Type header |
CONTENT_LENGTH |
Request Content-Length |
All HTTP request headers are also available as HTTP_* variables (e.g.,
HTTP_USER_AGENT, HTTP_ACCEPT).
The built-in navbar uses Python scripting to highlight the current page:
^navlinks:
script: python
code: |
import os, html as _html
page = os.environ.get('REQUEST_URI', '/')
link = record.get('key', '')
text = record.get('value', '')
active = ' active bg-primary bg-opacity-10' if link == page else ''
print(f'<li class="nav-item">'
f'<a class="nav-link{active}" '
f'href="{_html.escape(link)}">'
f'{text}</a></li>')
This reads the current page URL from REQUEST_URI and adds Bootstrap's
active class to the matching navigation link.
bserver wraps your code in a language-specific boilerplate:
import json, sys
_data = json.loads(sys.stdin.read())
if not isinstance(_data, list): _data = [_data]
for record in _data:
# your code here (indented 4 spaces)
const _data = JSON.parse(require('fs').readFileSync(0, 'utf8'));
const _records = Array.isArray(_data) ? _data : [_data];
for (const record of _records) {
// your code here
}
$_data = json_decode(file_get_contents('php://stdin'), true);
if (!is_array($_data)) $_data = [$_data];
foreach ($_data as $record) {
// your code here
}