open_api
The open_api library provides a first-pass implementation for
deriving, parsing, generating, and structurally validating OpenAPI 3.1.0
documents. It builds on top of the json, json_schema, and
application libraries.
This initial implementation focuses on deriving OpenAPI documents from
objects implementing the open_api_provider_protocol protocol using a
small, portable descriptor vocabulary. This provides the following
benefits:
Earlier validation: malformed descriptors can be rejected by functor shape and argument checks before any JSON normalization.
Better portability: provider objects depend on a Logtalk-level vocabulary, not on OpenAPI object field spelling.
Better reuse: the same descriptors could later feed both OpenAPI generation and a future authentication or HTTP integration layer.
Cleaner provider code: provider objects avoid raw camelCase OpenAPI keys and nested curly terms for common security cases.
Layering
open_api is a companion library for the HTTP stack rather than a
transport or dispatch layer:
Use it standalone when you only need to parse, generate, or structurally validate OpenAPI documents.
Pair it with the
http_corelibrary when you need request or response contract validation against normalized HTTP terms or wire sources.Pair it with
http_routerlibrary when router objects exposeopen_api_provider_protocolmetadata derived fromroute/4,route_metadata/2, androute_produces/2.Pair it with
restlibrary when you want the same validation and document derivation over higher-level endpoint descriptors built on top ofhttp_router.Use it directly with custom provider objects when your API description source is not a router or REST object.
API documentation
Open the ../../apis/library_index.html#open_api link in a web browser.
Loading
To load this library, load the loader.lgt file:
| ?- logtalk_load(open_api(loader)).
Testing
To test this library, load the tester.lgt file:
| ?- logtalk_load(open_api(tester)).
Current scope
Derive OpenAPI 3.1.0 documents from provider objects implementing the
open_api_provider_protocolprotocolConstruct reusable JSON descriptor terms using helper predicates for
media/2,request_body/3, andresponse/3, validating JSON-compatible media types, response status keys, and shallow schema-term shape, including boolean schemasResolve provider operations by
operationIdusingoperation/3Validate normalized HTTP request and response terms, or objects implementing the
http_request_protocolandhttp_response_protocolprotocols, against provider operation descriptorsParse HTTP request and response wire sources through the
http_corelibrary before validating them against provider operation descriptorsParse and generate OpenAPI documents using the repository-standard JSON term representation
Perform structural validation of derived or parsed documents using an inline phase-1 JSON Schema
Reject provider descriptor sets that reuse the same
operationIdatom for more than one operationDerive top-level OpenAPI
securitydefaults from providersecurity/1Derive reusable security schemes under
components.securitySchemesand reject top-level or operationsecurity/1references to undeclared scheme names or undeclared OAuth flow scopes when locally availableReject malformed reusable security scheme descriptors that omit fixed fields required by their
type, and reject malformed declared OAuth flow objects that omit their required URLs orscopesReject provider-side invalid
apiKey.invalues and malformed URL-valued security fields such as OAuth token or authorization endpointsValidate provider server descriptor URLs and reject malformed values
Accept wildcard response status descriptors such as
'2XX'Reject provider path parameter descriptors that are not declared in the operation path template or that do not use
Required = trueReject parsed or derived documents whose inline path parameters violate the OpenAPI path-parameter invariants
Accept webhook-only OpenAPI 3.1.0 documents during structural validation
Current limitations
Validation is intentionally partial and does not attempt to implement the full OpenAPI 3.1 meta-schema. Only selected type-specific fixed-field checks, such as those for Security Scheme Objects and OAuth Flow Objects, are provided
The library derives only
components.schemasandcomponents.securitySchemesplus the rootsecurityfieldThe library integrates with the
http_corelibrary for request and response parsing and for protocol-based access to normalized HTTP messages, but it limits itself to contract validation rather than transport concernsThe supported
security_scheme/2descriptor vocabulary is limited to the forms summarized belowScope validation cross-checks against locally declared OAuth flow scopes when they are present in a reusable security scheme. Full OpenID Connect discovery-based scope resolution is out of scope
Query and cookie parameter validation relies on typed request properties such as
query_pairs/1andcookies/1; raw query-string parsing is intentionally out of scopeRequest and response body validation supports
json/1,text/1,binary/1, andform/1payload terms; multipart payload validation remains out of scopeThe JSON helper predicates validate media types and response status keys eagerly, but schema validation at the helper boundary is intentionally shallow and only checks for boolean schemas, JSON object terms, or
schema_ref/1references
Provider descriptors
API metadata
The api_info/1 predicate returns an
info(Title, Version, Summary, Properties) descriptor for the
top-level OpenAPI info object. Currently recognized metadata
properties are limited to description(Description).
Provider objects that also implement application_protocol can
contribute missing OpenAPI metadata:
description/1maps toinfo.descriptionlicense/1maps toinfo.license.namehomepage/1maps to the top-levelexternalDocs.url
Server descriptors
The servers/1 predicate returns a list of
server(URL, Description) descriptors used to build the top-level
OpenAPI servers array. URL must be a valid OpenAPI Server Object
URL string, allowing absolute URLs, relative references, and templated
variables such as https://{username}.example.com:{port}/{basePath}.
Top-level security defaults
The security/1 predicate returns a list of top-level OpenAPI
security requirement alternatives. The returned list uses the same
Requirements representation accepted by operation
security(Requirements) properties: each requirement alternative is a
list of Scheme-Scopes pairs and [] denotes an empty security
requirement object. Scheme names must match declarations exposed by
security_scheme/2.
Operation descriptors
The operations/1 predicate returns a list of
operation(Id, Method, Path, Summary, Parameters, RequestBody, Responses, Properties)
descriptors. Id is used as the OpenAPI operationId and must be
unique across the returned descriptor list.
Nested descriptors are
parameter(Name, In, Description, Required, Schema),
request_body(Description, Required, MediaTypes),
response(Status, Description, MediaTypes), and
media(MediaType, Schema). Status may be an integer HTTP status
code, default, or a wildcard range atom such as '2XX'. Path
parameters must match a template expression in Path and must use
Required = true.
The current implementation keeps the operation property vocabulary narrow and supports only:
description(Description)tags(Tags)deprecated(Boolean)security(Requirements)whereRequirementsis a list of requirement alternatives represented as lists ofScheme-Scopespairs and[]denotes an empty security requirement object
Scheme names referenced from security(Requirements) must match names
exposed by security_scheme/2, and operation-level security overrides
any top-level security/1 declaration.
Reusable schemas
The schema/2 predicate maps reusable schema names to JSON Schema
curly terms compatible with the json_schema library. References to
reusable schemas are expressed using schema_ref(Name) terms.
Reusable security schemes
The security_scheme/2 predicate maps reusable security scheme names
to portable descriptor terms. This library accepts those terms and
normalizes them to OpenAPI Security Scheme Objects when deriving the
final document. The scheme names must match names referenced from
operation security(Requirements) properties. The descriptor
vocabulary keeps provider objects at the Logtalk domain level instead of
exposing OpenAPI JSON surface spelling directly:
provider code does not need raw OpenAPI field names such as
openIdConnectUrlorclientCredentialsvalidation can happen at the level of Logtalk terms before document generation
the same provider facts are easier to reuse in a future portable HTTP or authentication library because they describe security intent rather than emitted OpenAPI object structure
Supported descriptor shapes are:
api_key(In, Name)api_key(In, Name, Options)http(Scheme)http(Scheme, Options)mutual_tlsmutual_tls(Options)oauth2(Flows)oauth2(Flows, Options)openid_connect(URL)openid_connect(URL, Options)
Supported scheme options are:
description(Description)for all scheme kindsbearer_format(Format)forhttp/2flows(Flows)foropenid_connect/2when locally declared scopes are needed without relying on OpenID Connect discovery; these local flow declarations are used only for provider-side security-reference validation and are not emitted into the final OpenAPI document
Supported OAuth flow descriptors are:
implicit(AuthorizationURL, Scopes)implicit(AuthorizationURL, Scopes, [refresh_url(URL)])password(TokenURL, Scopes)password(TokenURL, Scopes, [refresh_url(URL)])client_credentials(TokenURL, Scopes)client_credentials(TokenURL, Scopes, [refresh_url(URL)])authorization_code(AuthorizationURL, TokenURL, Scopes)authorization_code(AuthorizationURL, TokenURL, Scopes, [refresh_url(URL)])
Scope descriptors are expressed as a list of
scope(Name, Description) terms.
Use descriptor terms such as:
security_scheme(
user_oauth,
oauth2([
client_credentials(
'https://auth.example.com/oauth/token',
[scope(read_users, 'Read user data')]
)
])
).
security_scheme(api_key, api_key(header, 'X-API-Key')).
security_scheme(user_bearer, http(bearer, [bearer_format('JWT')])).
security_scheme(user_oidc, openid_connect('https://example.com/.well-known/openid-configuration')).
An OpenID Connect scheme can also declare local scopes for the existing security reference validation path:
security_scheme(
user_oidc,
openid_connect(
'https://example.com/.well-known/openid-configuration',
[flows([
authorization_code(
'https://example.com/oauth/authorize',
'https://example.com/oauth/token',
[
scope(read_users, 'Read user data')
]
)
])]
)
).
Those local flow declarations are used only for provider-side security
reference validation. They are not emitted into the derived OpenAPI
document, as OpenID Connect Security Scheme Objects do not define a
flows field.
Provider-side validation checks, among other things:
duplicate scheme names
invalid descriptor functors or arities
required fixed fields after normalization
valid
apiKeylocationsmalformed OAuth flow or scope descriptors
malformed URL-valued fields such as
tokenUrl,authorizationUrl,refreshUrl, andopenIdConnectUrl
Usage
Load the library and derive a document from a provider object:
| ?- logtalk_load(open_api(loader)),
open_api::document(sample_open_api_provider, Document).
Parse and generate OpenAPI documents in JSON term form:
| ?- open_api::parse(atom('{"openapi":"3.1.0","info":{"title":"Demo","version":"1.0.0"},"paths":{}}'), Document).
| ?- open_api::generate(atom(JSON), {openapi-'3.1.0', info-{title-'Demo', version-'1.0.0'}, paths-{}}).
Validate a document and inspect any structural errors:
| ?- open_api::validate_document(Document, Errors).
Resolve an operation descriptor and validate normalized requests, responses, or HTTP wire sources against it:
| ?- open_api::operation(sample_open_api_provider, update_user, Operation).
| ?- open_api::validate_request(
sample_open_api_provider,
update_user,
request(
put,
origin('/users/11111111-1111-1111-1111-111111111111'),
http(1, 1),
[],
content('application/json', json({name-'Alice Example', active-true})),
[query_pairs([verbose-true])]
),
Errors
).
| ?- open_api::validate_response(
sample_open_api_provider,
get_user,
response(
http(1, 1),
status(404, 'Not Found'),
[],
content('application/json', json({code-not_found, message-'User not found'})),
[]
)
).
| ?- open_api::validate_http_request(
sample_open_api_provider,
update_user,
atom('PUT /users/11111111-1111-1111-1111-111111111111?verbose=true HTTP/1.1\r\ncontent-type: application/json\r\n\r\n{"name":"Alice Example","active":true}')
).
Construct standard JSON request and response descriptors when building provider operations programmatically:
| ?- open_api::json_request_body_descriptor(
'Message payload',
true,
{type-object, properties-{title-{type-string}}, required-[title], additionalProperties- @false},
RequestBody
).
| ?- open_api::problem_response_descriptor(default, 'Problem response', Response).