OxL Reference

10.1 Services

oxl
@v(2) pub(api) service Users {
  @readonly @cache(ttl: USER_CACHE_TTL)
  rpc get(id: UserId) -> User | NotFound

  @idempotent(key: r => r.requestId) @retry(STD_RETRIES)
  rpc create(requestId: RequestId, name: Name, email: Email?) -> User

  @resumable
  rpc watch(id: UserId) -> stream<UserEvent>
}

impl Users {
  dep db: Db
  rpc get(id) { db.find(User, id) ?? fail NotFound{ what: "user" } }
  rpc create(requestId, name, email) { … }
  rpc watch(id) { UserEvents.subscribe(key: id) }
}
  • A service declares a contract: a set of rpcs with named parameters and a result type. The parameters form the request message, the result the response message (tags per §9.2).
  • A program MUST contain exactly one impl for each service, unless it is declared extern service (a client of an external service, §16.3).
  • In an impl the parameter and result types MAY be omitted — they are taken from the service. The handler's effects are not restricted.
  • The handler's dependencies are declared with dep name: Type (§12.7).
  • Result errors are transmitted as typed gRPC error details. The status code comes from @grpc_status(code) on the error type, FAILED_PRECONDITION by default. NetError ↔ statuses: Timeout — DEADLINE_EXCEEDED, Unavailable — UNAVAILABLE, Cancelled — CANCELLED, AccessDenied — PERMISSION_DENIED/UNAUTHENTICATED, ProtocolError — the rest.

Calling:

FormRouting
Users.get(id)through the cluster (§12.3): locally if the node serves Users, otherwise over the network
Users.at(node).get(id)to a specific node of the cluster
Users.connect(addr, tls: opts)a client to an external address; returns Client<Users>

The context ctx is available in the handler and is propagated to outgoing calls: ctx.caller: Principal, ctx.deadline, ctx.traceId, ctx.meta: Map<str, str>, ctx.node.

duplicate-impl, impl-signature-mismatch, missing-impl, missing-rpc-impl, unknown-rpc

See also

Source: docs/spec/oxl-standard.en.md