Using Rib in API definition
Rib can be used in the API definition to define the response of an API endpoint.
The response
field in the API definition is a Rib script that is executed to generate the response for the API endpoint.
With Rib, it is also possible to lookup values from the request context, such as request path, query variables, headers, and JWT token claims.
Response Mapping
The response
field under each binding is written using Rib to call the worker function, manipulate its results,
and return a value that's understandable for each protocol we use. If the protocol is HTTP,
the result of Rib is converted to an HTTP response.
Example: Below is a Rib script where the result type is a record with two fields: status
and body
,
along with headers, which correspond to the status code, response headers, and body in an HTTP response.
let result = golem:it/api.{get-cart-contents}();
{ status: 200u64, body: result, headers: { Content-Type: "application/json" } }
Example API definition with more Rib scripts
Here is a more complex example of an API definition with multiple routes and
and more complex Rib scripts under response
field. Certain fields are omitted for brevity.
id: my-shopping-cart-v2
draft: true
version: 2.0.11
routes:
- method: Get
path: "/{user-id}/get-cart-contents"
binding:
response: |
let result = golem:it/api.{get-cart-contents}();
{status: 200u64, body: result}
- method: Post
path: "/{user-id}/initialize-cart"
binding:
response: |
let empty: list<u16> = [];
golem:it/api.{initialize-cart}(request.path.user-id);
{status: 200u64, body: empty}
- method: Post
path: "/{user-id}/add-item"
binding:
response: |
let quantity: f64 = request.body.quantity;
let product_id: u64 = request.body.product-id;
let name: string = request.body.name;
let price: f64 = request.body.price;
let input = {product-id: product_id, name: name, price: price, quantity: quantity};
golem:it/api.{add-item}(input);
{status: 200u64, body: success}
- method: Post
path: "/{user-id}/checkout"
binding:
response: |
let result = golem:it/api.{checkout}();
let status = 200u64;
let body = match result { success(resp) => resp.order-id, error(msg) => msg };
{status: status, body: body}
Most of the above examples are self explanatory, but worth pointing out a few details.
Passing parameters to worker functions
For example, in the initialize-cart
endpoint, we pass the user-id
from the request path to the worker function.
golem:it/api.{initialize-cart}(request.path.user-id)
Here Rib compiler knows the requirement of initialize-cart function and infers that request.path.user-id should be u64. This is a hint, that Rib is aware of the types in the code. In places where it find hard to infer these types, you may face some compile time errors (when uploading API definition), and the best solution is to be explicit about types. For example
let user_id: u64 = request.path.user-id;
golem:it/api.{initialize-cart}(user_id)
Handling response from worker functions
There are various possibilities here to manipulate the data using Rib
language, such as selecting only a particular field from the result of the function invocation. Say for example, the result is a WASM result
which can be ok
or err
. In this case you
can have a complex Rib expression as below
let result = golem:it/api.{get-cart-contents}();
let response_status = match result {
ok(_) => 200u32,
err(_) => 400u32
};
let response_body = match result {
ok(result) => result,
err(msg) => msg
}
{status: response_status, body: response_body}
The result of the Rib expression is whatever the result of the last line/expression in the above Rib program, similar to many programming languages.
Note, the last line of Rib is not ending with a ;
.
Lookup up values from the request context
Rib allow to look up the values of request path or query variables using request.path.*
syntax.
For request body, you can use request.body.*
syntax. Similarly for headers it is request.headers.*
If security is enabled, then you can also look up the JWT token claims using request.auth.*
, such as
request.auth.email
to get the email of the user. This is already shown in most of the examples above.