B.A.S.E. (Backend Automation & Scripting Environment) is a simple, standalone programming language for writing backend logic and server scripts.
It's built in Go and compiles to a single 22MB binary. There are no dependencies to install, no npm, and no pip. Just download the binary and you can run HTTP servers, query databases, send Discord webhooks, and automate your machine.
Run this to download the binary and install it to /usr/local/bin:
curl -fsSL https://raw.githubusercontent.com/igorkalen/base/main/install.sh | bashCheck if it works:
base --versionStart the interactive REPL to test things out:
baseRun a file:
base my_script.baseCreate a new web server project (creates a folder with a server.base file):
base new my-api
cd my-api
base runRun a quick one-liner:
base -e 'print("Hello world")'B.A.S.E. comes with a big standard library. Everything below works out of the box.
Build APIs with an Express-like routing system. Define routes, then start your server.
// Express-style routing — define routes, then start
server.route("/", function(req, res) {
res.html(200, "<h1>Welcome to B.A.S.E.</h1>")
})
server.route("/api/users", function(req, res) {
res.cors() // enable CORS
res.json(200, {"users": ["Igor", "Alice"]})
})
server.route("/health", function(req, res) {
res.json(200, {"status": "ok"})
})
server.start(3000, {
"read_timeout_ms": 60000,
"write_timeout_ms": 120000,
"idle_timeout_ms": 300000
})
Available optional timeouts for server.start(port, options?) (all in milliseconds):
| Option key | Description |
|---|---|
read_header_timeout_ms |
Maximum time to read HTTP headers before timing out |
read_timeout_ms |
Maximum time to read the full request, including body |
write_timeout_ms |
Maximum time to write the response |
idle_timeout_ms |
Maximum time to keep idle connections open |
Method-specific routes and static file serving:
// Serve a folder of HTML/CSS/JS files
server.static(8080, "./public")
// Method-specific routes (auto-start)
server.get(3000, "/api/items", function(req, res) {
res.json(200, [{"id": 1, "name": "Item 1"}])
})
server.post(3000, "/api/items", function(req, res) {
let body = req.body
res.json(201, {"created": body})
})
Response helpers available in every handler:
| Helper | Description |
|---|---|
res.send(status, body) |
JSON response |
res.json(status, data) |
Pretty JSON response |
res.html(status, html) |
HTML response |
res.text(status, text) |
Plain text response |
res.file(status, path) |
File with auto MIME type |
res.redirect(status, url) |
HTTP redirect |
res.header(key, value) |
Set custom header |
res.cors(origin?) |
Enable CORS (* by default) |
Request object available in every handler:
| Property | Description |
|---|---|
req.method |
HTTP method (GET, POST, etc.) |
req.path |
Request URL path |
req.query |
Query parameters as hash |
req.headers |
Request headers as hash |
req.body |
Parsed JSON body |
req.body_raw |
Raw body as string |
Send requests to other APIs. It supports custom headers, timeouts, and retries.
let data = http.get("https://api.example.com/data", {
"headers": {"Authorization": "Bearer token123"},
"timeout": 5,
"retries": 3
})
Parse and stringify JSON data.
let obj = json.parse('{"name": "Igor", "age": 25}')
print(obj.name)
let str = json.stringify({"key": "value"})
print(str)
Connect to SQLite, MySQL, PostgreSQL, or MongoDB natively.
db.connect("main", "sqlite", "./data.db")
db.exec("main", "CREATE TABLE users (name TEXT)")
db.insert("main", "users", {"name": "Igor"})
db.insert_many("main", "users", [{"name": "Alice"}, {"name": "Bob"}])
let users = db.query("main", "SELECT * FROM users")
Send messages directly to a Discord channel or via an SMTP email server.
// Send a message to a Discord webhook
notify.discord("https://discord.com/api/webhooks/...", "Server deployment finished!")
// Send an email (host, port, username, password, to_email, subject, body)
notify.email("smtp.gmail.com", "587", "you@gmail.com", "password", "target@email.com", "Alert", "Server is down!")
Spawn tasks to run in the background. chan() creates a thread-safe channel to collect the results.
let results = chan()
spawn function() {
let ping = http.ping("https://google.com")
results.send(ping)
}()
wait_all()
print(results.read_all())
Read/write files, generate UUIDs, and encrypt files using AES-256-GCM.
let id = crypto.uuid()
// Encrypt a file
crypto.encrypt_file("aes-256-gcm", "secrets.json", "my-password")
// Read a file
let content = file.read("config.json")
Run functions automatically on a schedule.
schedule("0 * * * *", function() {
log("Running hourly task", "INFO")
})
Place these at the top of any .base file to control runtime behaviour.
| Directive | Description |
|---|---|
//!no-keepalive |
Exit when the script finishes, even if a server or schedule is active |
//!no-keepalive
// This script starts a server to do one task, then exits
server.route("/migrate", function(req, res) {
res.json(200, {"done": true})
})
server.start(3000)
The syntax looks like JavaScript or C, but uses let for variables.
// Variables
let username = "Igor"
let active = true
let config = {"debug": true, "retries": 3}
let tags = ["dev", "admin"]
// Null & null coalescing
let x = null
let val = x ?? "default" // "default"
// String escape sequences: \n \t \r \\
let multiline = "line1\nline2\nline3"
// Template strings
let greeting = `Hello ${username}, you have ${len(tags)} tags!`
// Functions
let calculate = function(x, y) {
return x + y
}
// Default parameters
let greet = function(who, msg = "Hello") {
return msg + ", " + who + "!"
}
// Arrow functions
let double = (x) => x * 2
let add = (a, b) => a + b
let block = (x) => {
let y = x * 10
return y + 1
}
// If / else if / else
if active {
print("Welcome " + username)
} else if username == "guest" {
print("Hello guest")
} else {
print("Unknown user")
}
// Ternary
let msg = active ? "online" : "offline"
// Match/switch expressions
let result = match msg {
case "online": { "User is here" }
case "offline", "away": { "User is gone" }
default: { "Unknown" }
}
// Loops
foreach t in tags {
print(t)
}
foreach i, t in tags {
print(i + ": " + t)
}
for (let i = 0; i < 5; i = i + 1) {
print(i)
}
foreach i in range(0, 10, 2) {
print(i) // 0, 2, 4, 6, 8
}
while active {
print("running")
active = false
}
// Destructuring
let {name, age} = {"name": "Igor", "age": 25}
let [first, second] = [10, 20]
// Spread operator
let merged = [...[1, 2], ...[3, 4]] // [1, 2, 3, 4]
// Enums
enum Color { RED, GREEN, BLUE }
print(Color.RED) // 0
// Try/catch + throw
try {
throw "something went wrong"
} catch (err) {
print(err.message)
}
// Imports
import "utils.base" as utils
// Concurrency
spawn someFunction()
wait_all()
// Globals (accessible across scopes)
global API_KEY = "abc123"
// Global middleware — runs before every route handler
server.middleware(function(req, res) {
res.header("X-Powered-By", "B.A.S.E.")
})
// Group routes under a prefix
server.group("/api/v1", function() {
server.get(3000, "/users", function(req, res) {
res.json(200, ["Igor", "Alice"])
})
server.get(3000, "/posts", function(req, res) {
res.json(200, [])
})
})
server.start(3000)
let matched = regex.match("[0-9]+", "abc123") // true
let found = regex.find("[0-9]+", "abc123def") // "123"
let all = regex.find_all("[0-9]+", "a1b2c3") // ["1", "2", "3"]
let replaced = regex.replace("[0-9]", "abc123", "X") // "abcXXX"
let parts = regex.split("[,;]", "a,b;c") // ["a", "b", "c"]
To see everything the language can do, type base help in your terminal. It lists all modules for http, db, file, math, list, crypto, string, csv, yaml, regex, ws, ssh, and more.
| Module | Functions |
|---|---|
http |
get, post, put, patch, delete, ping |
db |
connect, query, insert, insert_many, update, delete, exec, aggregate |
server |
route, start, get, post, put, patch, delete, static, listen, middleware, group |
file |
read, write, append, exists, delete, list, mkdir, replace, json_update |
crypto |
uuid, hash, encrypt_file, decrypt_file |
sys |
exec, timestamp, version |
math |
abs, sqrt, pow, round, sin, cos, log, min, max, floor, ceil, random |
string |
upper, lower, replace, slice, pad_left, split, trim, contains, starts_with, ends_with, index_of |
list |
map, filter, sort, contains, length, reverse, reduce, join, slice, flat, push, index_of |
json |
parse, stringify |
csv |
read, write |
yaml |
read, write |
regex |
match, find, find_all, replace, split |
encode / decode |
base64 |
ssh |
exec |
ws |
connect |
notify |
discord, email |
chan |
create thread-safe channels |
archive |
zip |
env |
get |
| Utilities | print, log, len, type, range, wait, wait_all, schedule |
If you have any questions, feel free to reach out to me at igor@igorkalen.dev.
- Clone the repo:
git clone https://github.com/igorkalen/base.git - Install dependencies:
go mod download - Make changes (built-ins are in
/evaluator, language rules are in/parserand/lexer) - Test it locally:
go run main.go tests/my_test.base - Open a Pull Request!
