mjl > sherpa > specification
Intro | API documentation | Consuming | Specification | Implementations | FAQ | Why
Sherpa is still work in progress! It works well, but the specification might still change.

Specification

A Sherpa API is provided at a URL, for example https://www.sherpadoc.org/example/. Sherpa API's are designed to be easy to implement on the server side, and easy to use on the client (browser) side. A Sherpa API is usually implemented using a library, but writing Sherpa support directly into your application is also a viable option. This document describes how Sherpa API's work and what you must do to implement a Sherpa API.

Sherpa API's summarized:

This specification continues with the URL endpoints a Sherpa API must implement. We use sherpaweb's "Example API" as an example: https://www.sherpadoc.org/example/.

GET <URL>/sherpa.json

An API describes itself by a JSON file at called sherpa.json. Try opening it. It provides a document like the following.

{
    "id": "example",
    "title": "Example API",
    "version": "0.0.1",
    "sherpaVersion": 0,
    "baseurl": "https://www.sherpadoc.org/example/",
    "functions": [
        "requestCount",
        "_docs"
    ]
}

A GET on the API URL must return a JSON document (as shown above). All fields are required and must not be null.

Field Type Description
id string Short ID for this API. The JavaScript library stores the API object in a global variable with this name. Must match regular expression "[a-zA-Z][a-zA-Z0-9_]+"
title string Full human-readable name of this API.
version string Descriptive version string. Could be in the form of major.minor.patch. But a single API endpoint should not really have different "major" versions at different times. Incompatible API versions are best deployed at a new endpoint.
sherpaVersion int Version of the Sherpa specification. This specification is still work in progress, that's why it is still at version 0. In the future, backwards compatible changes (e.g. adding fields to the JSON object describing the API) may be made without increasing the version number. For incompatible changes, the version number will be increased. If a client library loads a Sherpa API with a version number is does not support (either too old or too new), it must generate an error.
baseurl string URL for this API.
functions array of string List of methods exported by the API. Parameter types and return types are not part of the API. Requiring specification of such types would make the API significantly more complex and much harder to keep programming language-independent. Most scripting languages would not make use of it anyway. The parameter and return value types should be specificied in the API documentation. Function names must match regular expression "[a-zA-Z_][a-zA-Z0-9_]+", though functions starting with an underscore have special meaning as described later.

GET <URL>/sherpa.js

This JavaScript library makes it trivial for frontend developers to start using a Sherpa API.

This library must set a global variable with the name of the "id" field from sherpa.json. This variable must be an object providing all functions exported by the API. Calling such a function from JavaScript must return a "thenable": an object with an attribute called then that has the signature "function(resolved, rejected) {...}". Resolved will be called with the result of a successful API call. Rejected will be called with an error object if the call failed. Error objects are described later in this document.

The thenable-objects can be transformed by setting the attribute _wrapThenable on the API object. By default, this is a function returning its parameter. Frontend developers may set this to a function that calls new Promise(thenable), for full promise-support.

The API object has a field _sherpa, containing the JSON document from sherpa.json.

POST <URL>/functionName

A HTTP POST to the API URL followed by the function name calls that function. The request body must be a JSON document with a params field, an array with parameters. An example:

{
    "params": [
	"test",
	123
    ]
}

The response is again a JSON document with fields result and/or error. A response must have at least one of those fields. Both can be present, but then one of them must be null.

On success, the result field contains the function return value. This value can be any valid JSON type, e.g. boolean, an array, an object, etc. The value of field "result" is passed as argument to the resolved function in the JavaScript library. An example:

{
    "result": ["limited", "imagination"]
}

On error, the error field contains an error object. An error object in turn contains at least the following fields:

An example error response:

{
    "error": {
        "message": "no permission to modify table X"
        "code": "user:permissionDenied"
    }
}

Error messages should start with a lower case letter and not end with a dot. This lets you easily combine (prepend/append) strings that create a full error message for display to the user.

For forward compatibility, Sherpa API developers should not provide function names starting with an underscore other than those specified in this document.

Error codes

HTTP status codes are not used to indicate success or failure. The POST should almost always return a status 200 "OK", so even if the function called returned an error. The exception is if the function name does not exist. In that case, the HTTP status should be 404 "File not Found". The rationale is that if an API disappears, or a programmer used an incorrect API URL, function calls would return status 404 as well, so Sherpa libraries should expect and gracefully handle 404's.

Errors at the protocol-level must have a code starting with "sherpa:". Errors not at the protocol level must use another prefix. Known codes:

sherpa:http
Unexpected HTTP-level error occurred, like incorrect HTTP response status code (i.e. not 200 and not 404).
sherpa:badFunction
Function does not exist in this API. This might be returned by the server, or generated by the client library code.
sherpa:badRequest
Bad request body. Invalid JSON, or missing "params"-field.
sherpa:badParams
Function was not called with the correct number/type of parameters.
sherpa:badResponse
The HTTP response status code indicated success, but the response could not be parsed (e.g. malformed JSON in the body).
sherpa:noAPI
No API seems to be available at this URL (it returned a 404).

If a function call resulted in an error that was the fault of the client, the server must use the "user:"-prefix.

Server libraries must generate error codes with the "server:"-prefix for unhandled errors and errors that are not directly the fault of the user.

GET <URL>/functionName

Instead of using HTTP POST, you can also call a function with a HTTP GET. An optional query string parameter body (defaulting to no parameters) in the same format as the POST JSON body can be set to pass parameters. The response is the same as for HTTP POST requests.

JSONP is also supported: If the query string parameter callback is present, the repsonse will be a JavaScript file that calls the specified callback with the response object as parameter.

GET <URL>/

A GET on the Sherpa API base URL can provide free-form, useful information about the API. A typical response is to show a minimal page explaining this is a Sherpa API, and that has the JavaScript API loaded for quick experimentation, followed by a link to the documentation and more information about Sherpa in general.

See sherpaweb's example: sherpadoc.org/example/.

Additional requirements

The endpoints above are all that is needed to implement a Sherpa API. This section lists additional requirements.

The HTTP responses must include CORS-headers allowing use from other domains. This means the HTTP OPTIONS method must also be supported.

All data in Sherpa is UTF-8. This includes JSON request and response bodies, and JavaScript. Sherpa API's should check that the Content-Type of POST function calls is application/json. If the "charset" is set, it should be "utf-8". In the future, other content-types may be treated differently.

All Sherpa function call responses should disallow caching by setting a response header "Cache-Control: no-store".

Sherpa API's do not have to support HTTP HEAD requests on function URLs.

Documentation

A Sherpa API provides documentation through the function _docs. It is a function like any other and as such can be called through a HTTP POST. The result is a JSON document that must adhere to a simple format. This format is called sherpadoc. Sherpaweb renders this document. The object is a toplevel "documentation"-object, that looks like this:

{
    "title": "Title for this section of the documentation",
    "text": "Body of the documentation for this section, as markdown.",
    "functions": [
        {
            "name": "requestCount",
            "text": "Return the number of times this function has been called since this API was last restarted.",
            "params": [
            ],
            "return": [
              {
                "name": "r",
                "type": ["int"]
              }
            ]
        },
        ... more functions ...
    ],
    "sections": [
       ... nested "documentation"-objects ...
    ],
    "types": [
      {
        "name": "User",
        "text": "User has a name and email and can log in to the system.",
        "fields": [
          {
            "name": "name",
            "type": ["string"],
            "text": "Full name of the user.",
          },
          {
            "name": "email",
            "type": ["string"],
            "text": "Email address of the user.",
          },
          {
            "name": "is_admin",
            "type": ["boolean"],
            "text": "Whether user is an admin.",
          },
        ]
      },
      ... more types ...
    ],
    "version": 1
}

Documentation can be hierarchically arranged. With a top-level documentation object introducing the API. And further topics divided in sections, each with their functions. The full, flattened list of provided functions must be specified in sherpa.json. The documentation provides functions explicitly so tools like sherpaweb can place the functions in the right section and provide functionality to call a function. Only the top-level section should have a "version"-field. Version-fields in subsections should be ignored.

Both the main text and function text fields should be markdown. This allows you to easily create structure, or even include HTML for richer documentation.

Parameters are specified just like return values: With a "name" and "type". Named types, in the "types" field, have a "name", "text" with documentation, and "fields", an array of fields. Each field has a "name", "text" as documention and a "type". The "type" field in parameters, return values and named types share a format: An array of type tokens. Valid tokens are: "nullable", "any", "bool", "int", "float", "string", "[]", "{}" and named type identifiers of the form [a-zA-Z][a-zA-Z0-9]*. Production rules:

basictype := "boolean" | "int" | "float" | "string"
array := "[]"
map := "{}"
identifier := [a-zA-Z][a-zA-Z0-9]*
type := "nullable"? ("any" | basictype | identifier | array type | map type)

Changes

Changes from earlier versions of this document:

Previous versions: