In-Database WebApps and REST Services

From MemCP
Revision as of 11:59, 28 August 2026 by Wikiservice (talk | contribs) (Refresh MemCP documentation: accuracy, operational guidance, performance profile and maintained API reference)
Jump to navigation Jump to search

In-Database WebApps and REST Services

Embedded handlers run in the MemCP process and can access Scheme and storage APIs without a separate database connection. lib/main.scm defines http_handler; application modules can wrap the previous handler and route only their own path prefix.

This is useful for small JSON APIs, dashboards, webhooks, static assets, or WebSocket gateways whose hot path is mostly database work. It reduces deployment pieces and transport overhead, but also couples application code to the database process. Keep CPU-heavy, blocking, untrusted, or independently deployed workloads outside MemCP.

Routing a path prefix

<syntaxhighlight lang="scheme"> (define http_handler (begin (set old_handler http_handler) (lambda (req res) (begin (match (req "path") (regex "^/my-api/(.*)$" path rest) (begin ((res "header") "Content-Type" "application/json") ((res "status") 200) ((res "print") "{\"ok\":true}")) (old_handler req res)))))) </syntaxhighlight>

Always delegate unmatched paths to the previous handler. Replacing the global handler without chaining it can hide the dashboard, SQL endpoints, or routes installed by other modules.

The request object exposes method, host, path, query fields, headers, username/password, remote address, and lazy body readers. The response object sets headers and status, writes text/lines or JSONL rows, and can upgrade a connection to WebSocket. See IO for the generated function reference and apps/ for executable examples.

Prepared SQL and response streaming

Parse and optimize fixed SQL outside the request hot path where practical; bind request values rather than concatenating untrusted SQL. The exact request/response functions are documented in IO and examples under apps/.

<syntaxhighlight lang="scheme"> (set find_user (parse_sql "myapp" "SELECT id, name FROM users WHERE id = @user_id"))

/* inside a request handler */ (set session (newsession)) (session "user_id" requested_id) (set resultrow (res "jsonl")) (eval find_user) </syntaxhighlight>

Prepared formulas capture a fixed query shape, not permission to trust their parameters. Validate request types, authorize the selected record, and do not let callers choose arbitrary identifiers. Stream rows when possible; do not collect an unbounded result merely to turn it into one JSON array.

The RDF frontend follows the same preparation pattern with parse_sparql. Prepare a fixed SPARQL shape outside the handler, bind request-specific values through the intended context and stream results through the response. Do not concatenate caller text into either SQL or SPARQL.

Error handling and service boundaries

Set the content type and status before writing the body. Convert expected validation and not-found cases into explicit 4xx responses; log unexpected failures without returning stack traces, SQL, secrets, or internal paths. Define request timeouts and body limits at the reverse proxy and inside application logic.

Security and lifecycle

Handlers execute with in-process capabilities. Authenticate before database access, validate paths, headers and bodies, enforce request-size limits, and never expose default root/admin credentials. Bound queues and long operations, propagate cancellation, and avoid retaining request objects after completion. Use --no-repl for daemon deployment. See Security and Authentication, SQL over REST, and Websockets in MemCP.

Minimal maintained examples live below apps/. The external rdfop project is a larger RDF browser and templating example; treat it as application source, not as part of MemCP's compatibility contract.