Resources
Components expose operations: named units of behavior, called with input and a query. The world outside an application mostly speaks HTTP, in terms of resources and methods. Something has to stand between the two.
In Toa that is the gateway, a service the runtime provides. A component does not contain an HTTP server, a router, or a controller. It declares how its operations appear as resources, and the gateway serves them.
§Declaring resources
A component describes a tree of routes and maps HTTP methods on them to its operations:
# orders/manifest.toa.yaml
exposition:
/:
GET: enumerate
POST: transit
/:id:
GET: observe
PUT:
endpoint: approve The tree is mounted under the component’s own name, so these become GET /orders/, POST /orders/, GET /orders/:id/, and PUT /orders/:id/. Route variables such as :id become part of the call’s query: GET /orders/42/ observes the order whose identity is 42.
The declaration belongs to the component. When a component is added to an application, its resources appear with it, and the gateway learns of them from the running components. There is no central routing file to keep in agreement with the code.
§Requests become calls
For each request the gateway builds one call to one operation:
| Request | Call |
|---|---|
| Path variables and query string | query |
| Body | input |
and turns the reply into a response. This is the input/query segregation of ordinary calls, seen from outside: the URL selects state, the body carries arguments.
The mapping respects what operations are. A method that reads is mapped to an operation that
reads, and a method that creates or changes to one that writes. Because GET starts a readonly chain, a route cannot quietly turn a read into a write.
Replies map to statuses by their kind, which is where the distinction drawn in Replies becomes visible to a client:
| Outcome | Response |
|---|---|
| Output | Success, with the output as the body. |
| Returned error | A client-side refusal, with the error as the body. |
| Request that does not fit the contract | Bad request. |
| Entity that does not exist | Not found. |
| Lost race or passed version | Conflict or precondition failed. |
| Exception | Server error. |
§What a route may narrow
A route is a public face of an operation, and it can be narrower than the operation itself.
/:
GET:
endpoint: enumerate
io:output: [id, status, total]
query:
criteria: status==pending
sort: CREATED:desc
limit: { value: 20, range: [1, 100] } This route lists pending orders only, newest first, at most a hundred at a time, and returns three properties of each. A client can refine the selection within those bounds and not beyond them.
Output is closed by default: a route returns only the properties it names. An entity gaining an internal field does not leak it through routes written before the field existed.
§Directives
Behavior around a call is declared on the tree as directives. A directive on a node applies to everything beneath it:
/:id:
auth:id: id
cache:control: max-age=60
GET: observe Directives come in families. The ones supplied with the gateway cover authorization, caching, restrictions on input and output, request mapping, file handling, and realtime streams.
The effect is that everything a client can observe about a resource — who may call it, what it accepts, what it returns, how it is cached — is stated in one place, beside the operation it exposes.
§The gateway is not the application
The gateway holds no business logic and no business state. It authenticates, authorizes, maps, calls, and responds. Whatever a request ends up doing is done by an operation, under the same rules as a call from another component.
Two consequences follow.
The HTTP surface is optional. A component with no exposition is fully functional inside
the application. Tests, other components, and scheduled work call operations directly.
The same resources can be offered in other forms. Since a route is a description of a callable thing, the gateway can present it through other protocols without anything being redeclared; see Procedures and Tools.
§A description of itself
Because the tree is declared data, the gateway can answer questions about it. A client can ask what a resource accepts and returns, or ask for every route it is allowed to call. The answer is computed from the same declarations that serve the requests, filtered by what the asking identity may reach, so it cannot drift from the behavior it describes.