Hyperion

Introduction

Hyperion is a lightweight specification for RESTful JSON APIs. It endeavors to provide guidance and convention for the variety of decisions API-writers need to make during their development. Hyperion also takes some inspiration from JSON-LD as a nod to hypermedia, but with emphasis on low-overhead to make it easier for API-writers to adopt.

Conformance

The keywords may, must, must not, recommended, should, and should not are to be interpreted as described in [RFC2119].

Conventions

Content Negotiation

Clients MUST send all JSON data in request documents with the header Content-Type: application/json.

Servers MUST send all JSON data in response documents with the header Content-Type: application/json.

URI

Anywhere a URI is specified, it must adhere to the following rules:

Sub-service Path

APIs MUST provide a sub-service path in the URI as a way to create a namespace for your endpoints.

A sub-service URI path MUST have the following:

These two rules promote clear differentiation of sub-services and versions, allowing independent development.

HTTP Status Codes

APIs MUST respond to requests with an Http Status Code that is found in the following list:

status code description error code notes
200 OK   The request has successfully completed processing
201 Created   Resource has been created and processed successfully
202 Accepted   The request has been accepted for processing, but the processing has not been completed.
204 No content   (for deletions only) The deletion process has completed
301 Moved Permanently Redirect requests permanently, and allow for changing method  
303 See Other Redirect requests temporarily, and allow for changing method  
304 Not Modified Implicit redirection to client’s cache  
307 Temporary Redirect Redirect requests temporarily, and do not allow changing method  
308 Permanent Redirect Redirect requests permanently, and do not allow changing method  
400 Bad Request invalid_input The request has one or more errors and cannot be processed
401 Unauthorized unauthorized The request did not include a required authentication component
403 Forbidden forbidden The request has failed authorization checks
404 Not found not_found Resource not found
405 Method not allowed method_not_allowed The http method was not valid at the specified URI
409 Conflict invalid_operation The request could not be completed due to a conflict with the current state of the target resource
413 Payload Too Large payload_too_large Request payload is too large and the server will not process it
422 Unprocessable Entity The request and it’s body are valid and parseable, but are malformed in a semantic way (missing data, incorrect structure)  
429 Too many Requests rate_limit_reached Too many requests have been received in a given amount of time
500 Internal server error internal_error The server cannot process the request for an unknown reason
502 Bad gateway bad_gateway The gateway server or proxy received an invalid response from upstream server while fulfilling a request
503 Service Unavailable service_unavailable The server is currently unable to handle requests temporarily due to maintenance or resource exhaustion from high load
504 Gateway timeout gateway_timeout The gateway server or proxy failed to receive a response from upstream server while fulfilling a request

Versioning

APIs MUST provide versioning in the URI path, following the sub-service path.

MUST increase either Major or Minor in the version path whenever a breaking change is introduced. /vMajor[.Minor]

https://api.company.com/sub-service/v1

https://api.company.com/sub-service/v1.1

Naming Conventions

Date Handling

All dates MUST be represented as string values following the ISO 8601 standard.

All dates MUST follow ISO 8601 standard of YYYY-MM-DD.

An example of date only value.

{
    "@id": "/users/1",
    "@type": "User",
    "given_name": "Hubert",
    "family_name": "Farnsworth",
    "date_of_birth": "1975-11-30"
}

All datetime MUST be UTC following the ISO 8601 standard of YYYY-MM-DDThh:mm:ss.sZ.

Note: The Z designator at the end is what expresses a datetime as UTC. It will be treated as local datetime without it.

An example of UTC datetime value.

{
    "@id": "/users/1",
    "@type": "User",
    "given_name": "Hubert",
    "family_name": "Farnsworth",
    "date_of_birth": "1975-11-30",
    "created_at": "2017-11-30T21:43:25Z"
}

Time Series

APIs that need to implement time series functionality MUST refer to specifications defined in the time series section.

An example of time series using relative time.

https://api.vdms.io/analytics/v1/time-series?start=now-1M&end=now

{
    "@id": "/analytics/v1/time-series?start=now-1M&end=now",
    "@type": "TimeSeries",
    "start": "2018-05-18T21:43:25Z",
    "end": "2018-06-18T21:43:25Z",
    ...
}

Keywords

Hyperion specifies a few keywords as part of the core specification:

{
    "@id": "/connect/userinfo/1",
    "@type": "UserInfo",
    "id": 1,
    "given_name": "Hubert",
    "family_name": "Farnsworth"
}

Note: To avoid compatibility issues, properties starting with an @ character are restricted as they might be used as keywords in future versions of JSON-LD. Properties starting with an @ character that are not JSON-LD keywords are treated as any other property, i.e., they are ignored. Keywords are case-sensitive.

Restricted keywords are: @context, @id, @value, @language, @type, @container, @list, @set, @reverse, @index, @base, @vocab, @graph, @nest, @prefix, @version, @links

Document Structure

All requests sent by the client and responses sent by the server MUST be a valid JSON document.

{
    ...
}

All JSON properties MUST follow naming conventions.

{
    "given_name": "Hubert",
    "family_name": "Farnsworth"
}

The top most JSON object MUST be a node and MUST contain an @id property unless creating a new resource.

{
    "@id": "/users/1",
    ...
}

An example of a node with query string parameters:

{
    "@id": "/users?page=1",
    ...
}

The top most JSON object MUST be a node and MUST contain a @type property.

{
    "@id": "/users/1",
    "@type": "User",
    "given_name": "Hubert",
    "family_name": "Farnsworth"
}

Node

A Node represents a JSON object. A Node MUST contain the @id property if it is the top most object. It SHOULD NOT be in the JSON object when creating a resource.

The @id property represents a valid URI and follows conventions described in the keywords section.

A Node MUST contain the @type keyword.

The value for @type property MUST be a string value representing the type following naming conventions.

An example of @type:

{
    "@id": "/users/1",
    "@type": "User",
    ...
}

A node MAY contain nested nodes.

{
    "@id": "/users/1",
    "@type": "User",
    "given_name": "Hubert",
    "family_name": "Farnsworth",
    "address" : {
        "@id": "/users/1/address",
        "@type": "Address",
        "street": "West 57th Street"
    }
}

A node MAY contain links with the keyword of @links.

{
    "@id": "/users/1",
    "@type": "User",
    "@links" : {
        "users": {
            "href": "/users"
        }
    },
    "given_name": "Hubert",
    "family_name": "Farnsworth"
}

Link

A Link is an object used to represent a collection of link value related to the resource.

A Link MUST have the following:

Link Value

A LinkValue is an object containing a valid URI.

A LinkValue MUST have the property href which represents a valid URI.

A LinkValue MAY have the following:

{
    "@id": "/users/1",
    "@type": "User",
    "@links" : {
        "users": {
            "href": "/users",
            "description": "Gets a collection of users"
        },
        "permissions": {
            "href": "/users/1/permissions",
            "description": "Gets a collection of user permissions",
            "base_path": "https://api.xyz.com/security"
        }
    },
    "given_name": "Hubert",
    "family_name": "Farnsworth"
}

Collection

A Collection is a type of node used to represent a resource returning many of the same kind of thing in a generic way. It MUST be the top most JSON object and MUST NOT be nested.

A Collection MUST have the following:

A Collection MAY have the following:

{
    "@id": "/users?page=2&page_size=4",
    "@type": "Collection",
    "@links": {
        "first": {
            "href": "/users?page=1&page_size=4"
        },
        "next": {
            "href": "/users?page=3&page_size=4"
        },
        "previous": {
            "href": "/users?page=1&page_size=4"
        },
        "last": {
            "href": "/users?page=5&page_size=4"
        }
    },
    "items": [
        {
            "@id": "/users/1",
            "@type": "User",
            "given_name": "Hubert",
            "family_name": "Farnsworth"
        },
        {
            "@id": "/users/2",
            "@type": "User",
            "given_name": "Philip",
            "family_name": "Fry"
        },
        ...
    ],
    "total_items": 20
}

Entry Point

An EntryPoint is a type of node used to represent a resource that clients can use to get more information about an API and provide links to traverse.

It MUST be the top most JSON object and MUST NOT be nested.

An EntryPoint MUST have the following:

A EntryPoint SHOULD have the following in the @links object:

An EntryPoint SHOULD have the following:

A EntryPoint MAY have the following:

{
  "@type": "EntryPoint",
  "@id": "/foo/v1",
  "@links": {
    "users": {
      "href": "/foo/v1/users",
      "description": "This is the users endpoint"
    },
    "customers": {
      "href": "/foo/v1/customers",
      "description": "This is the customer endpoint"
    },
    "documentation":{
        "href": "/",
        "base_path": "https://developer.foo.io"
    } ,
    "support": {
        "href": "/support",
        "base_path": "https://developer.foo.io"
    }
  },
  "name": "Foo v1 API",
  "description": "Description about Foo API",
  "version": "v1"
}

Error

Processing errors can be handled by returning a node of Error to consumers. In addition, an appropriate HTTP status code, a human readable error code must be returned.

An Error MUST have the following:

An Error MAY have the following:

Error Detail

Some errors require more information about what failed and sometimes where the error occurred. API authors can then provide a node of ErrorDetail to help pin-point specific errors.

An ErrorDetail MUST have the following:

An ErrorDetail MAY have the following:

{
    "@type": "Error",
    "code": "invalid_request",
    "status_code": 400,
    "title": "One or more properties were empty or invalid",
    "description": "One or more required fields were empty or not meeting validation requirements.",
    "details": [
        {
            "@type": "ErrorDetail",
            "source": "/first_name",
            "description": "Must not be empty and a minimum of 4 characters"
        },
        {
            "@type": "ErrorDetail",
            "@links": {
                "account": {
                    "href": "/v1/account",
                    "base_path": "https://api.xyz.co",
                    "description": "This is the account endpoint"
                }
            },
            "source": "/account_id",
            "description": "Must not be empty"
        }
    ]
}

Error Codes

code description
unauthorized User or client is not authorized (invalid access token)
forbidden User or client does not have the correct oauth2 scopes
not_found Resource does not exist or client does not have permission to that resource
invalid_operation Request sent has a conflict with the current state of a resource
invalid_input User or client sent a request that the server could not understand due to invalid syntax or did not meet validation requirements
internal_error Internal server error occurred
rate_limit_reached Too many requests have been received in a given amount of time
payload_too_large Payload too large
method_not_allowed Method not allowed
internal_error The server cannot process the request
bad_gateway The gateway server or proxy received an invalid response from upstream server while fulfilling a request
service_unavailable The server is currently unable to handle requests temporarily due to maintenance or resource exhaustion from high load
gateway_timeout The gateway server or proxy failed to receive a response from upstream server while fulfilling a request