I First Steps

Expose It over HTTP

The shop’s website has to list orders, show one, create one and approve one. A component serves no HTTP itself: it declares which of its operations appear as HTTP resources, and the gateway, a service of the runtime, serves them.

The orders are those of store.orders, the component Create a Component writes: create makes an order, approve approves a pending one, and enumerate and observe are inherited.

§Declare the resources

Add an exposition section to the manifest:

# components/store.orders/manifest.toa.yaml
exposition:
  /:
    anonymous: true
    io:output: true
    GET: enumerate
    POST: create
    /:id:
      GET: observe
      PUT: approve

The component’s routes are mounted under /<namespace>/<name>:

RequestCalls
GET /store/orders/store.orders.enumerate
POST /store/orders/store.orders.create, with the body as input
GET /store/orders/<id>/store.orders.observe of that order
PUT /store/orders/<id>/store.orders.approve of that order
  • :id is a route variable; its value selects the order.
  • io:output: true lets replies through. Without it a request succeeds with 204 No Content and no body.
  • anonymous: true lets anyone call these routes. Without an access rule a route answers 401 Unauthorized.

Both are written once on / and apply to everything nested in it.

Opening the whole API to everyone is fine for a first run and for nothing else. Signing users in starts at Sign Users Up and In; the rules that say who may call what are in Restrict Who Can Call a Route.

§Run the application

The gateway is part of sys, one of the two processes npm start keeps under PM2, and it listens on port 8000. It learns the routes from the components, so restart:

$ npm run restart

The host the API is served on is named in the Context:

# context.toa.yaml
exposition:
  authorities:
    local: localhost:8000

§Send requests

Create an order:

$ curl -i http://localhost:8000/store/orders/ 
    -H 'content-type: application/json' 
    -d '{"customer":"c1","items":[{"product":"kettle","quantity":1}],"total":4200}'
HTTP/1.1 201 Created
content-type: application/json

{"id":"01a10df7b5e47692b3c64a94cff78bfd","VERSION":1,"DELETED":null,"REGION":0,"status":"pending","CREATED":1791235700196,"UPDATED":1791235700196,"customer":"c1","items":[{"product":"kettle","quantity":1}],"total":4200}

Read it, this time as YAML. The gateway answers in the format the accept header asks for, and reads bodies in the format content-type names:

$ curl -H 'accept: application/yaml' http://localhost:8000/store/orders/01a10df7b5e47692b3c64a94cff78bfd/
VERSION: 1
DELETED: null
REGION: 0
status: pending
CREATED: 1791235700196
UPDATED: 1791235700196
customer: c1
items:
  - product: kettle
    quantity: 1
total: 4200
id: 01a10df7b5e47692b3c64a94cff78bfd

List the orders, then approve the one just created:

$ curl http://localhost:8000/store/orders/
$ curl -i -X PUT http://localhost:8000/store/orders/01a10df7b5e47692b3c64a94cff78bfd/
HTTP/1.1 200 OK
content-type: application/json

{"VERSION":2, … "status":"approved", … }

§An error over HTTP

Approve the same order again. The error the operation returns is answered with 422 and its code as the body:

$ curl -i -X PUT http://localhost:8000/store/orders/01a10df7b5e47692b3c64a94cff78bfd/
HTTP/1.1 422 Unprocessable Entity
content-type: application/json

{"code":"NOT_PENDING"}

Other answers you will meet early:

StatusWhen
400 Bad RequestThe body does not fit the operation’s input. The body of the response says why: "must have required property 'total'".
401 UnauthorizedThe route has no access rule that lets the caller in.
404 Not FoundThere is no such order, or no such route. A path without the trailing slash is answered "Trailing slash is required".
405 Method Not AllowedThe route does not declare the method.

§More

Search
Search the documentation