Osy#betaRun it on Osyrin: osyrin.com

Reference / Function

Crossing the network

An Osy# app runs in two places — the server, which holds the data, and the browser, which holds the screen — and the runtime moves between them in the middle of a body. A call is written the same way either way; what a crossing costs is one round trip, and what it changes is nothing else.

stable2 examples compiled by CIfunctionuiruntimeauthoring

Summary#

An Osy# app runs in two places. The server holds the data, the secrets and the authority to enforce security. The browser holds the screen, the router and the person using it. A single function body can need both, and the runtime moves between them as the body runs.

Description#

A call is a call#

Calling something that runs in the other place is written exactly like calling anything else. You write Login(email, password), and the platform evaluates the arguments where you are, suspends, runs the body where the work is, and resumes you with the result.

That includes failure. An exception raised over there arrives here as an exception, so a try/catch wrapped around a crossing behaves the way the same code would if nothing had crossed at all:

try {
  var token = Login(email, password);      // the body runs on the server
  Navigation.Go("/home");                  // this line is back in the browser
} catch (Exception e) {
  error = e.Message;                       // and so is this
}

One body, both places#

A body is not confined to one place. It can read and validate on the server and then ask the person something in the browser, in one straight line, and the runtime hands each part to whichever engine has what it needs:

[Route("/orders")]
component OrderPage() {
  action Cancel(Order order) {
    if (Confirm(order)) {                  // ↩ reads and validates on the server…
      Navigation.Go("/orders");            //   …and this line comes back to the browser
    }
  }
}

What crosses arrives unchanged#

A value that crosses is the same value. A decimal keeps every digit and does not become a floating-point approximation; a long above 2^53 survives intact; a DateTime keeps its instant. A comparison that is true on one side is true on the other, and a total adds up to the same number wherever it was summed. Text behaves identically down to the awkward cases — Text.Upper treats the German ß the same in both engines.

So a crossing is not a conversion, and nothing needs to be re-checked on arrival.

Where the data is#

The data store lives on the server, so a query over an entity reaches the server to answer:

var c = Customer.Single(x => x.Email == email);   // the rows are on the server

A query over a list you already hold is not a data read at all — the list is already here, and filtering it is ordinary work on a value in hand:

int Cheap(List<Line> lines) {
  return lines.Where(l => l.Price < 10).Count();
}

The standard library works the same way: Text.Upper, Text.Trim, s.Length, name.Contains("x") and the arithmetic operate on a value you are already holding, so they happen in place. A handful of calls need something only the server has — Security.HashPassword needs the host's salt generator, Security.IssueJwt its signing key, Crypto.Encrypt a key the browser must never hold — and those reach the server for it. See The standard library — and where each call runs for the full table of which is which.

What a crossing costs#

Time, and nothing else. Work that happens in place costs nothing; a crossing is one round trip, and a body that crosses several times pays for several. That is the whole of the difference — the answers are identical either way, so the cost is a latency question and never a correctness one.

Examples#

string Initials(string first, string last) {
  return first.Substring(0, 1) + last.Substring(0, 1);
}
entity Customer { string Email; string Name; }

string NameFor(string email) {
  return Customer.Single(c => c.Email == email).Name;
}

See also#

Related

function

A function is a top-level unit of work, written like a C# method — a return type, a name, typed parameters, a body. It…

LINQ over a local list

Query a local `List<T>`, `HashSet<T>` or `T[]` — of your own `class` values OR of plain scalars like `string[]` and…

component

The one archetype for all UI: a bounded reactive unit — typed props, reactive members (fields, `live`…

The standard library — and where each call runs

The pure standard library, and the one fact about it that changes how your app feels: 271 of its 396 methods run in the…