BUILD BACKENDS

HTTP & storage

Routes, authentication, lifecycle hooks, SQLite and MongoDB.

An HTTP project declares apps instead of a global main. Each app owns its helper functions, storage handles and declared environment capabilities. Route sets are combined into one native server.

Create a service

app api {
    base "/api";
    budget connections 32;
    get "/hello/{name}" -> Response {
        return response_header(
            response(200, json({ "hello": param("name") })),
            "cache-control", "no-store"
        );
    }
}
ryft build main.rz -o api.exe
.\api.exe --port 8080 --data-dir .\data
Invoke-RestMethod http://127.0.0.1:8080/api/hello/Ryft

Routes support GET, POST, PUT, PATCH, DELETE, HEAD and OPTIONS. String/Json routes return 200; Response selects a status and body. Use param, query, header, request_text or request_json to read a request. JSON parsing alone does not enforce Content-Type or domain validation.

Shared authentication guard

app private_api {
    permit env "API_TOKEN";
    before {
        if !secure_eq(header("authorization"), "Bearer " + env("API_TOKEN")) {
            return response(401, json({ "error": "unauthorized" }));
        }
    }
    get "/private" -> Response {
        return response(200, json({ "ok": true }));
    }
}

Supply the declared secret in the process environment before starting. Missing/empty declared variables prevent startup. A before block is copied into every route in that app; any early return must match the route’s declared type. Place public liveness routes in another app when appropriate. A single bearer token is service authentication, not a user account or roles system.

SQLite persistence and transactions

app database {
    storage db: sqlite("tasks.db");
    startup {
        let setup = db_exec(db, "CREATE TABLE IF NOT EXISTS tasks (id INTEGER PRIMARY KEY, title TEXT NOT NULL)", []);
        assert(as_bool(json_get(setup, "ok")));
    }
    post "/tasks" -> Response {
        let result = db_transaction(db, [
            { "sql": "INSERT INTO tasks (title) VALUES (?)", "params": ["First"] },
            { "sql": "INSERT INTO tasks (title) VALUES (?)", "params": ["Second"] }
        ]);
        return response(200, result);
    }
}

The database resides at <data-dir>/database/tasks.db. SQLite uses WAL, foreign keys and app-scoped paths. SQL templates must be literals; bind request values as parameters. db_transaction commits a bounded batch atomically or rolls it back. Always inspect ok; a JSON operation envelope does not automatically select an HTTP error status. ATTACH, application PRAGMAs and explicit SQL transaction statements remain blocked.

MongoDB

app documents {
    permit env "MONGODB_URI";
    storage db: mongodb("MONGODB_URI", "my_service");
    get "/ready" -> Json { return mongo_ping(db); }
    get "/tasks" -> Json {
        return mongo_find(db, "tasks", {}, 50, 0, { "_id": -1 });
    }
}

Set MONGODB_URI privately in the environment, including credentials and deployment options. For Atlas, allow the server’s outbound address in Network Access and grant the database user only the required privileges. TLS certificate verification stays enabled. Never commit a real URI or embed it in source.

Collections must be source literals. MongoDB ObjectIds encode as hexadecimal strings; use Extended JSON {"$oid":"..."} in filters when matching an ObjectId. DateTime values encode as epoch milliseconds. Read/write operations return ok envelopes. See standard library for signatures.

mongo_transaction supports insert/update/delete steps in one session on a compatible deployment. A failed operation aborts the batch. An unknown commit outcome can return uncertain: true; reconcile before retrying rather than blindly replaying writes. Synchronous driver calls are not forcibly cancelled at the HTTP deadline.

Lifecycle and isolation

Startup runs after storage initialization and before readiness. Shutdown runs after a graceful drain, but cannot run after a crash, OOM or forced termination. Hooks cannot read request state, including through helpers.

--isolate uses supervised Windows app workers and Job Object resource limits. Without it, apps share one process and memory/CPU declarations are metadata. budget connections caps active requests, not TCP connections. Logical app ownership is not a complete hostile-code sandbox.

Logs, metrics and deployment

The runtime assigns x-request-id, emits JSON access logs, and provides /_ryft/metrics and isolated worker health at /_ryft/health. Keep diagnostic endpoints behind trusted ingress. POST /_ryft/shutdown accepts an authenticated parent control token; provision it privately rather than exposing the control endpoint publicly.

Use a suitable HTTPS reverse proxy, persistent data directories and a dedicated account for deployment. Database migrations, user identity, rate limits and backup policy are application/operations responsibilities. See Windows deployment and production release gates.

Ryft 0.3.0Source: docs/http-and-storage.md

Search installation, types, routes, storage and more.