Skip to content
NorscodeNorscode

std.web

ReferenceBy the Norscode project

std.web is everything you need to build web services, and the largest module in the standard library with around a hundred functions: read the request, validate it, check access, and build the response.

std.web is everything you need to build web services, and the largest module in the standard library with around a hundred functions. It covers the whole way: reading what comes in on a request — method, path, query string, headers, cookies and body — validating it, checking roles and tokens for access, and building the response that goes out. On top of that there is routing, middleware, guards and helpers for safe text and safe file paths.

A web service in Norscode is simply functions that answer requests. You mark a function with a route, read what you need from the request context ctx, and return a response. Because the whole layer lives in the standard library, you need neither an external framework nor a separate server alongside. The functions below are grouped by what you do with them — start with the family you need.

When to use it

Use std.web when you make an HTTP service. A route is a function that calls web.route, reads what it needs from ctx with the request_ functions, and returns a response built with response_. Together with the net.tcp capability and the nc serve command you have a running service.

Getting started

bruk std.web som web

funksjon hei(ctx: ordbok_tekst) -> ordbok_tekst {
    web.route("GET /")
    la navn = web.request_query_param(ctx, "navn")
    hvis navn == "" { navn = "verden" }
    returner web.response_builder(200, {"content-type": "text/plain; charset=utf-8"}, "Hei, " + navn + "!")
}

funksjon start() -> heiltall {
    skriv("Tjenesten er klar")
    returner 0
}

The route GET / is answered by the function hei. It reads an optional ?navn= parameter and builds a response with a status code, headers and content. Start the service with nc serve.

The functions

Reading the request

Everything starts with what comes in. These functions pull the individual parts of the request out of ctx: which method and path were used, the query string as a whole or one parameter at a time, headers, and the body. request_query_required is the strict variant that fails clearly if something mandatory is missing, while request_query_param just returns empty text.

  • request_context(metode: tekst, sti: tekst, query: ordbok_tekst, headers: ordbok_tekst, body: tekst) -> ordbok_tekst — Builds a request context — most useful in tests.
  • request_method(ctx: ordbok_tekst) -> tekst — The HTTP method used (GET, POST, …).
  • request_path(ctx: ordbok_tekst) -> tekst — The path in the request.
  • request_query(ctx: ordbok_tekst) -> ordbok_tekst — The whole query string as a map.
  • request_headers(ctx: ordbok_tekst) -> ordbok_tekst — All request headers as a map.
  • request_body(ctx: ordbok_tekst) -> tekst — The raw request body as text.
  • request_params(ctx: ordbok_tekst) -> ordbok_tekst — All path parameters as a map.
  • request_param(ctx: ordbok_tekst, nøkkel: tekst) -> tekst — One path parameter as text.
  • request_param_int(ctx: ordbok_tekst, nøkkel: tekst) -> heltall — A path parameter interpreted as an integer.
  • request_query_param(ctx: ordbok_tekst, nøkkel: tekst) -> tekst — The value of one query parameter, or empty text.
  • request_query_required(ctx: ordbok_tekst, nøkkel: tekst) -> tekst — Like request_query_param, but fails if the parameter is missing.
  • request_query_int(ctx: ordbok_tekst, nøkkel: tekst) -> heltall — A query parameter interpreted as an integer.
  • request_header(ctx: ordbok_tekst, nøkkel: tekst) -> tekst — The value of one header.
  • request_id(ctx: ordbok_tekst) -> tekst — A unique id for the request, useful in logs.

JSON in the request

If the client sends JSON in the body, you do not have to parse it yourself. request_json gives the whole body as a map, and request_json_field* fetches one field at a time — with typed variants for numbers and booleans, and the _or variant that returns a default if the field is missing.

  • request_json(ctx: ordbok_tekst) -> ordbok_tekst — The body interpreted as a JSON map.
  • request_json_field(ctx: ordbok_tekst, nøkkel: tekst) -> tekst — One field from the JSON body as text.
  • request_json_field_or(ctx: ordbok_tekst, nøkkel: tekst, fallback: tekst) -> tekst — One JSON field, with a default if it is missing.
  • request_json_field_int(ctx: ordbok_tekst, nøkkel: tekst) -> heltall — One JSON field interpreted as an integer.
  • request_json_field_bool(ctx: ordbok_tekst, nøkkel: tekst) -> boolsk — One JSON field interpreted as a boolean.

Cookies

Cookies are how a service remembers a visitor between requests. Read them with request_cookie (or request_cookie_or with a default), and set them in the response with response_set_cookie. cookie_header builds the header text itself if you need it raw.

  • request_cookie(ctx: ordbok_tekst, nøkkel: tekst) -> tekst — The value of one cookie.
  • request_cookie_or(ctx: ordbok_tekst, nøkkel: tekst, fallback: tekst) -> tekst — A cookie value, with a given default if it is missing.
  • cookie_header(nøkkel: tekst, verdi: tekst) -> tekst — Builds the text for a Set-Cookie header.
  • response_set_cookie(response: ordbok_tekst, cookie: tekst) -> ordbok_tekst — Adds a cookie to the response.

Authentication and roles

Protect routes without writing the access control yourself. bearer_token extracts the token from the Authorization header, and require_bearer rejects requests that do not carry the right one. For role-based access, role, has_role and require_role give you the role, a check, and a hard block respectively — and has_permission/require_permission do the same for finer permissions.

  • auth_header(ctx: ordbok_tekst) -> tekst — The Authorization header raw.
  • bearer_token(ctx: ordbok_tekst) -> tekst — Bearer token extracted from the Authorization header.
  • require_bearer(ctx: ordbok_tekst, expected: tekst) -> boolsk — True if the request carries the expected bearer token.
  • role(ctx: ordbok_tekst) -> tekst — The role attached to the request.
  • has_role(ctx: ordbok_tekst, rolle: tekst) -> boolsk — True if the request has the given role.
  • require_role(ctx: ordbok_tekst, rolle: tekst) -> boolsk — Requires a specific role; used to protect routes.
  • has_permission(ctx: ordbok_tekst, rettighet: tekst) -> boolsk — True if the request has a given permission.
  • require_permission(ctx: ordbok_tekst, rettighet: tekst) -> boolsk — Requires a given permission.

Building responses

Once you have done the work, you build a response. response_builder is the base form: status code, headers and content. Around it are shortcuts for the most common cases — response_html, response_text_plain and response_json set the right content type for you, response_redirect sends the client on, and response_file/response_static_file deliver files. The response_finalize variants finish the response with strict security headers.

  • response_builder(status: heltall, headers: ordbok_tekst, body: tekst) -> ordbok_tekst — The base form of a response: status code, headers and content.
  • response_status(response: ordbok_tekst) -> heltall — Reads the status code from a response.
  • response_code(response: ordbok_tekst) -> heltall — Sets the status code on a response.
  • response_headers(response: ordbok_tekst) -> ordbok_tekst — Reads the headers from a response.
  • response_body(response: ordbok_tekst) -> tekst — Reads the body from a response.
  • response_text(response: ordbok_tekst) -> tekst — A plain-text response.
  • response_with_header(response: ordbok_tekst, nøkkel: tekst, verdi: tekst) -> ordbok_tekst — Adds one header to a response.
  • response_html(status: heltall, body: tekst) -> ordbok_tekst — A response with HTML and the right content type.
  • response_text_plain(status: heltall, body: tekst) -> ordbok_tekst — A response with text/plain.
  • response_json(response: ordbok_tekst) -> ordbok_tekst — A response with JSON and the right content type.
  • response_redirect(location: tekst, status: heltall) -> ordbok_tekst — Sends the client on to another address.
  • response_redirect_found(location: tekst) -> ordbok_tekst — Redirect with status 302.
  • response_redirect_see_other(location: tekst) -> ordbok_tekst — Redirect with status 303.
  • response_no_content() -> ordbok_tekst — Empty response with status 204.
  • response_error(status: heltall, melding: tekst) -> ordbok_tekst — A standardised error response with the given status.
  • response_file(sti: tekst, content_type: tekst) -> ordbok_tekst — Delivers a file as the response.
  • response_static_file(root: tekst, rel_sti: tekst, content_type: tekst) -> ordbok_tekst — Delivers a static file safely from a directory.
  • response_header(response: ordbok_tekst, nøkkel: tekst) -> tekst — Reads one header from a response.
  • response_finalize(ctx: ordbok_tekst, response: ordbok_tekst) -> ordbok_tekst — Finishes the response with standard security headers.
  • response_finalize_strict(ctx: ordbok_tekst, response: ordbok_tekst) -> ordbok_tekst — Finishes with strict security headers.
  • response_finalize_strict_cors(ctx: ordbok_tekst, response: ordbok_tekst, tillatne_origins: tekst) -> ordbok_tekst — Finishes strictly, with CORS headers.

Validation and error responses

Good validation fails early and explains why. The *_or_error functions fetch a value and at the same time give a ready-made error response if it is missing or has the wrong type, so you do not have to handle each case by hand. validation_error_response and validation_bad_request build standardised 400 responses.

  • validation_error_body(errors: liste<tekst>) -> tekst — Builds the body of a validation-error response.
  • validation_error_response(errors: liste<tekst>, status: heiltall) -> ordbok_tekst — A complete 400 response for validation errors.
  • validation_bad_request(errors: liste<tekst>) -> ordbok_tekst — A simple 400 response with a message.
  • request_query_required_or_error(ctx: ordbok_tekst, nøkkel: tekst, errors: liste<tekst>) -> tekst
  • request_header_or_error(ctx: ordbok_tekst, nøkkel: tekst, errors: liste<tekst>) -> tekst
  • request_header_int_or_error(ctx: ordbok_tekst, nøkkel: tekst, errors: liste<tekst>) -> heiltall
  • request_header_bool_or_error(ctx: ordbok_tekst, nøkkel: tekst, errors: liste<tekst>) -> boolsk
  • request_json_int_or_error(payload: ordbok_tekst, nøkkel: tekst, errors: liste<tekst>) -> heiltall
  • request_json_text_or_error(payload: ordbok_tekst, nøkkel: tekst, errors: liste<tekst>) -> tekst
  • request_json_bool_or_error(payload: ordbok_tekst, nøkkel: tekst, errors: liste<tekst>) -> boolsk
  • request_json_list_or_error(payload: ordbok_tekst, nøkkel: tekst, errors: liste<tekst>) -> liste<tekst>
  • request_json_object_or_error(payload: ordbok_tekst, nøkkel: tekst, errors: liste<tekst>) -> ordbok_tekst
  • response_shape_validate_or_error(payload: ordbok_tekst, schema: ordbok_tekst, errors: liste<tekst>)
  • request_param_int_or_error(params: ordbok_tekst, nøkkel: tekst, errors: liste<tekst>) -> heiltall

Routing and dispatch

Routing links a path to a function. route is what you use in each handler; router and subrouter let you organise larger services. path_match, path_params and path_param interpret parts of the path, and dispatch/handle_request are the machinery that sends a request to the right handler — useful when you build something outside the standard flow.

  • route(spec: tekst) -> tekst — Registers which method and path the function answers on.
  • router(prefiks: tekst) -> tekst — Creates a router to organise several routes.
  • subrouter(prefiks: tekst) -> tekst — A subrouter with a shared prefix.
  • path_match(mønster: tekst, sti: tekst) -> boolsk — True if a path matches a pattern.
  • path_params(mønster: tekst, sti: tekst) -> ordbok_tekst — Extracts named parts out of a path.
  • route_match(spec: tekst, metode: tekst, sti: tekst) -> boolsk — Matches a request against a route.
  • path_param(params: ordbok_tekst, nøkkel: tekst) -> tekst — One named part of the path.
  • path_param_or(params: ordbok_tekst, nøkkel: tekst, fallback: tekst) -> tekst — A path part, with a default if it is missing.
  • dispatch(routes: ordbok_tekst, metode: tekst, sti: tekst) -> tekst — Sends a request to the right handler.
  • dispatch_params(routes: ordbok_tekst, metode: tekst, sti: tekst) -> ordbok_tekst — Like dispatch, with path parameters.
  • handle_request(ctx: ordbok_tekst) -> ordbok_tekst — Handles a whole request through the router.

Middleware, guards and lifecycle

Middleware runs before or after each request — logging, timing, common headers — without repeating the code in every route. guard and use_guard place a barrier in front of routes, dependency/use_dependency give shared resources (such as a database connection) to the handlers, and the startup/shutdown hooks run code when the service starts and stops.

  • guard() -> tekst — Defines a barrier that can be placed in front of routes.
  • use_guard(navn: tekst) -> tekst — Activates a barrier for routes.
  • dependency(navn: tekst) -> tekst — Defines a shared resource the handlers can receive.
  • use_dependency(navn: tekst) -> tekst — Fetches a shared resource in a handler.
  • request_dependency(ctx: ordbok_tekst, nøkkel: tekst) -> ordbok_tekst — Fetches a dependency attached to the request.
  • request_middleware() -> tekst — Code that runs before each request.
  • response_middleware() -> tekst — Code that runs after each response.
  • error_middleware() -> tekst — Code that runs when a handler fails.
  • startup_hook() -> tekst — Registers code that runs at startup.
  • shutdown_hook() -> tekst — Registers code that runs at shutdown.
  • startup() -> heltall — Runs the startup hooks.
  • shutdown() -> heltall — Runs the shutdown hooks.

Safe text and paths

Anything from outside can be hostile. escape_html makes user text safe to show in HTML, and safe_filename, safe_slug, safe_path_segment, safe_relative_path and safe_join_path make sure a filename or a path from a user can never point outside where you want it.

  • escape_html(tekst_verdi: tekst) -> tekst — Makes text safe to show in HTML.
  • safe_filename(tekst_verdi: tekst) -> tekst — Cleans a filename from the user.
  • safe_path_segment(tekst_verdi: tekst) -> tekst — Cleans one part of a path.
  • safe_slug(tekst_verdi: tekst) -> tekst — Makes a safe slug from text.
  • safe_relative_path(rel_sti: tekst) -> boolsk — True if a relative path stays within the root.
  • safe_join_path(root: tekst, rel_sti: tekst) -> tekst — Joins a path without being able to point outside the root.

Documentation and server

openapi_json and docs_html generate machine-readable and human-readable documentation of your routes, and web_server_start starts the server when you are not using nc serve directly.

  • openapi_json(tittel: tekst, versjon: tekst) -> tekst — Generates an OpenAPI description of the routes.
  • docs_html(tittel: tekst, versjon: tekst) -> tekst — Generates readable documentation of the routes.
  • web_server_start(ruter: liste<tekst>, vert: tekst, port: heltall) -> heltall — Starts the web server programmatically.

Good to know

  • Each route is its own function that calls web.route — the runtime has no dynamic path routes, so one function per fixed path; use query parameters for variation.
  • Always read user data through the request_ functions and build responses with response_; then you avoid handling the HTTP details yourself.
  • Use require_role/require_bearer to protect routes, escape_html before you show user text, and remember that the service needs net.tcp to bind a port.

std.web is one of the modules in the standard library. Everything ships with the runtime — no installation, no external dependencies. See also Documentation for the language and the runtime.

Related

Back to the overview