httpc call convention
The convention
The httpc call convention describes a set of rules to structure an http request so it can be easily translated into a function call.
An httpc call is an abstraction over the HTTP protocol. It shapes the http request in order to:
- identity which function to call
- send the function arguments
- receive the function result
The httpc call convention provides a simple way to build function-based APIs without worrying much about the underling HTTP complexity.
The httpc call convention defines two Conformity Levels:
A request must conform to all convention rules, otherwise it will be rejected. This is useful when receiving requests from httpc-aware clients like
HttpCClient
or the custom client generated from an API.The server goes around some rules and tries to understand the request even when not strictly following the convention. Loose mode can be used to accept standard http requests from browsers, rest client or server webhooks.
The Rules
The rules aim to identify:
the function to execute
The http request path is used as call path, that is, the unique id of any server function.
Examples
When a request is sent to
https://domain/products/getById
the server expects a function on
/products/getById
to be defined.A server can also bind to a subpath, for example
/api
, so when a request is sent tohttps://domain/api/orders/create
the server expects a function on
/orders/create
to be defined.the function arguments
In the basic usage, the http verb doesn’t play a role. So by convention, the POST verb is used for any function call.
The arguments are sent with the request body as a json serialized array, with a content-type header equals to
application/json
. When the function has no arguments, an empty array[]
is sent.Examples
The function call
add(1, 2)
corresponds to the http request:POST /add Content-Type: application/json [1, 2]
Same, with a single-argument function call
update({ name: "new-name" })
POST /update Content-Type: application/json [{"name": "new-name"}]
Advanced usage involves both GET and POST verbs, read the following section for details.
the function result
The exact response status code has no relevance. An httpc client will evaluate the response with:
- status < 400: result is a success
- status >= 400: result is an error
An httpc server will respond with a content-type of
application/json
. The result will be parsed from the body as json. The whole body is the result. If the body is empty, the result isundefined
.When the result is a success, the parsed value is returned to the caller. If the result is an error, an httpc client will throw a
HttpCClientError
with the body as error data.More details on Error result section.
Advanced Rules
In case interoperability with http-level cache is required, the httpc call convention defines two call access: "read"
and "write"
. Because http caching works only with the GET verb, the call access "read"
adopts the GET to send the request.
The convention establishes:
read call
The request is sent using the GET verb. The request path still identifies the call path, that is, the unique function to invoke.
Function arguments are sent via query string. The
$p
param is a json serialization of the arguments.Examples
The function call
getPost("id-10")
corresponds to the http request:GET /getPost?$p=["id-10"]
A no-argument function call
getLatestPost()
can avoid to send the query string:GET /getLatestPost
write call
The default call access and follows the basic rules mentioned in the previous section.
The request is sent using the POST verb.
A call assess is simply a way to send the underlying http request. With no specification, the default call access "write"
is used. The call access "read"
is introduced with the single purpose of interoperability with the http-level cache.
The names "read"
and "write"
are just a convention and don’t define different behaviors. On httpc level, there’s no difference, both call access produce the same invocation with the same processing.
HTTP vs HTTPC
// TODO
Http verb
Headers
response status code
Error result
When a request fails, an httpc server sends the error details back to the client as json with the response body. The content-type header will be application/json
.
When the response has status >= 400
, it’s considered an error. An httpc client will throw a HttpCClientError
with the relevant info.
An error is defined with:
property | type | description |
---|---|---|
status | number | the http response status code |
error | string | an application-defined error code |
message | string? | optional message to explain the error |
data | object? | optional extra data for context |
Examples
A basic error response defines the request status code and an error
.
{
"status": 401,
"error": "unauthorized"
}
An optional message can be sent to better explain the error.
{
"status": 404,
"error": "not_found",
"message": "The article(U-NkrLT2) is not found"
}
Extra data can be added to provide error context.
{
"status": 400,
"error": "bad_request",
"message": "Some parameter are not valid",
"data": {
"username": "Must be at least 10 char"
}
}
Call access
Read call
A read
call uses the http GET verb and sends arguments via a json-serialized query string $p
parameter.
httpc call --read call/path arg1 arg2 argN
GET /call/path?$p=[ serialized-parameters ]
import { HttpCClient } from "@httpc/client";
const client = new HttpCClient();
const result = await client.read("call/path", arg1, arg2, ...argN);
function sendReadCall(callPath: string, parameters: any[]) {
const url = this.apiEndpoint + "/" + callPath + "?$p=" + JSON.stringify(parameters);
return fetch(url, { method: "GET" });
}
Write call
A write
call uses the http POST verb and sends arguments via body with a json-serialized content.
httpc call call/path arg1 arg2 argN
POST /call/path
Content-Type: application/json
[ serialized-parameters ]
import { HttpCClient } from "@httpc/client";
const client = new HttpCClient();
const result = await client.write("call/path", arg1, arg2, ...argN);
function sendWriteCall(callPath: string, parameters: any[]) {
const url = this.apiEndpoint + "/" + callPath;
return fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json"
}
body: JSON.stringify(parameters)
});
}
Conformity level
Strict mode
In strict mode any http requests must conform to all httpc call convention rules:
- http verb must be either GET or POST
- http verb must match the call access:
- for
read
calls the http verb must be GET - for
write
calls the http verb must be POST
- for
- function arguments must be passed:
- for
read
calls via query string parameter$p
and must be a serialized json array - for
write
calls via body content and must be a serialized json array, with a content-type header set toapplication/json
- for
read
calls the query string parameter$p
can be omitted - for
write
calls the body can be empty
- for
In strict mode, the server will reject the non-conformant requests with a BadRequestError
.
Loose mode
In loose mode, the server will parse the http request in a more lenient way. Loose mode is useful when you need to adapt the parsing with some additional flexibility.
In loose mode, the server can accept:
the http verb can be any of GET, POST, PUT or PATCH with no match required to the relative
call access
.for GET requests:
- if the query string has the
$p
param, it will parsed as in strict mode - otherwise, the query string will be transformed in an object where each query param will be an object field
Example
The query string:
GET /search?term=test&language=en
will be parsed into:
search({ term: "test", language: "en" });
- if the query string has the
for requests with body (POST, …):
- if the body contains a non-array value(
string
,number
,boolean
,object
), it will be used as the first and unique argument for the function call
- if the body contains a non-array value(
In loose mode, the server still accepts strict httpc requests. In other words, loose mode are additional rules on top the strict mode ones.