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:

  1. identity which function to call
  2. send the function arguments
  3. 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:

The Rules

The rules aim to identify:

  1. 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 to https://domain/api/orders/create


    the server expects a function on /orders/create to be defined.


  2. 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.


  3. 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 is undefined.


    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:


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:

propertytypedescription
statusnumberthe http response status code
errorstringan application-defined error code
messagestring?optional message to explain the error
dataobject?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.

import { HttpCClient } from "@httpc/client";

const client = new HttpCClient();
const result = await client.read("call/path", arg1, arg2, ...argN);

Write call

A write call uses the http POST verb and sends arguments via body with a json-serialized content.

import { HttpCClient } from "@httpc/client";

const client = new HttpCClient();
const result = await client.write("call/path", arg1, arg2, ...argN);

Conformity level

Strict mode

In strict mode any http requests must conform to all httpc call convention rules:

  1. http verb must be either GET or POST
  2. 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
  3. 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 to application/json
    if no argument is necessary for the call, they can omitted:
    • for read calls the query string parameter $p can be omitted
    • for write calls the body can be empty

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:

  1. the http verb can be any of GET, POST, PUT or PATCH with no match required to the relative call access.

  2. 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"
    });
  3. 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

In loose mode, the server still accepts strict httpc requests. In other words, loose mode are additional rules on top the strict mode ones.