________________________________________________________________________

This file is part of Logtalk <https://logtalk.org/>
SPDX-FileCopyrightText: 1998-2026 Paulo Moura <pmoura@logtalk.org>
SPDX-License-Identifier: Apache-2.0

Licensed under the Apache License, Version 2.0 (the License);
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an AS IS BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
________________________________________________________________________


`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](../../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
