# `rest`

The `rest` library provides a small REST authoring layer on top of the existing `http_router` library. It is implemented as a category so that REST application objects can continue to implement the `http_handler_protocol` protocol directly while reusing the existing router dispatch, request annotation, middleware, and OpenAPI derivation logic.

This library reuses the `http_core` and `http_router`. It derives router hooks from higher-level endpoint descriptors, provides small request and response helpers, and normalizes simple action result terms into HTTP responses.

## Layering

`rest` is the highest-level authoring layer in the current HTTP stack:

- Start with `http_core` library for normalized HTTP messages and with the `http_router` library for declarative routing.

- Use the `rest` library when you want to describe endpoints with `endpoint/5`, keep the inherited `handle/2` entry point, and return small result terms instead of building every response manually.

- Stay with the `http_router` library when you need direct control over `route/4`, custom handler predicates, or router hooks without the higher-level endpoint layer.

- Pair REST applications with the `open_api` library when you want derived OpenAPI documents or opt-in request and response contract validation.

## API documentation

Open the ../../apis/library_index.html#rest link in a web browser.

## Loading

To load the library, load the `loader.lgt` file:

    | ?- logtalk_load(rest(loader)).

## Testing

To test this library, load the `tester.lgt` file:

    | ?- logtalk_load(rest(tester)).

## Current scope

The current version provides one imported routing predicate through the category inheritance from `http_router` plus request and response helper predicates:

- `handle/2`

- `path_parameter/3`

- `query_parameter/3`

- `request_header/3`

- `request_body/2`

- `json_body/2`

- `json_object_body/2`

- `json_array_body/2`

- `form_body/2`

- `text_body/2`

- `binary_body/2`

- `json_response/4-5`

- `created_response/4`

- `no_content_response/2`

- `problem_response/6`

The category also exposes optional OpenAPI contract validation hooks:

- `open_api_validate_request/1`

- `open_api_request_validation_error_response/4`

- `open_api_validate_response/1`

- `open_api_response_validation_error_response/5`

The category also exposes optional action failure and error normalization hooks:

- `rest_action_failure_response/3`

- `rest_action_error_response/4`

Importing REST application objects are expected to define endpoint descriptors using:

- `endpoint(Id, Method, Path, Action, Options)`

Endpoint identifiers must be unique within the importing object. When the object also exposes OpenAPI operations through `http_router`, those same identifiers are reused as operation identifiers.

The `Action` argument is the name of a declared local predicate with arity 2, typically a protected predicate. The action predicate receives the annotated request and returns or throws one of the currently supported result terms:

- `ok(JSON)`

- `created(Location, JSON)`

- `no_content`

- `json(Status, JSON)`

- `json(Status, Headers, JSON)`

- `problem(Status, Type, Title, Detail)`

- `response(Response)` or a normalized `response/5` term directly

The category derives the `http_router` hooks from those endpoint descriptors:

- `route/4`

- `route_metadata/2`

- `route_produces/2`

All matched routes dispatch through a single internal action runner that calls the endpoint action and normalizes its returned or thrown result.

Endpoint options are passed through as route metadata except for the special `produces(MediaTypes)` option, which is translated into `route_produces/2`. This keeps endpoint metadata available to action predicates and response middleware and also allows router-level OpenAPI derivation to keep working.

That same metadata pass-through also allows REST applications to reuse router companion categories. For example, an object can import `http_router_digest_auth(_, _, _)`, declare `digest_auth(Options)` in endpoint options, delegate `authorize_routed_request/2` to `authorize_digest_auth_request/2`, and register `add_digest_authentication_info/3` as response middleware.

The current helper predicates read the normalized HTTP request terms provided by `http_core`:

    request(Method, Target, Version, Headers, Body, Properties)

They rely on router annotations such as `route/1` and `path_params/1` plus the normalized derived properties produced by the `http_core` library such as `query_pairs/1`. The `path_parameter/3` and `query_parameter/3` helpers are deterministic lookups. The decoded body helpers return `400 Bad Request` problem responses when the current action expects a JSON, form, text, or binary body and the normalized request body is missing or has a different decoded shape. The `json_object_body/2` and `json_array_body/2` helpers build on `json_body/2` and additionally require the decoded JSON payload to be an object or array term respectively.

Action result normalization currently builds normalized HTTP response terms from returned or thrown supported result terms using these rules:

- successful JSON responses use the negotiated router media type when the request carries a JSON-compatible `response_media_type/1` annotation and otherwise fall back to `application/json`

- problem responses always use `application/problem+json`

Plain action failure and unsupported exceptions still default to generic `500 Internal Server Error` problem responses, but importing objects can override those defaults using `rest_action_failure_response/3` and `rest_action_error_response/4`.

When `open_api_validate_request/1` succeeds for a matched endpoint, the category validates the normalized request against the derived OpenAPI operation descriptor before calling the endpoint action. When `open_api_validate_response/1` succeeds for a matched endpoint, the category validates the normalized response after action result normalization.

By default, request validation failures preserve three classes of client error: unsupported request media types return `415 Unsupported Media Type`, schema-invalid request bodies return `422 Unprocessable Content`, and other request validation failures return `400 Bad Request`. Response validation failures still return a `500 Internal Server Error` `application/problem+json` response. Importing objects can override those defaults using the corresponding validation error response hooks. Validation is skipped for the synthetic router requests used internally for OpenAPI request and response inference.

## OpenAPI integration

When an importing object also implements the `open_api_provider_protocol` protocol, the inherited `http_router` OpenAPI derivation continues to work. Endpoint options such as `summary/1`, `description/1`, `tags/1`, `deprecated/1`, `security/1`, `parameters/1`, `request_body/1`, and `responses/1` are exposed through `route_metadata/2`, while `produces(MediaTypes)` drives `route_produces/2` and therefore response media type inference.

## Current limitations

- Request and response OpenAPI contract validation is opt-in per endpoint using the validation hooks

- The library does not include dedicated pagination, filtering, or rate-limiting helpers of its own

- Result normalization focuses on JSON and problem responses; it does not provide specialized helpers for redirects, streaming, or multipart payloads