White Paper

WHITE PAPER

04/2016

WSO2 Rest API Design Guidelines

By Frank Leymann, Ph.D. , Joseph Fonseka, Sanjeewa Malalgoda, Nuwan Dias, Sameera Medagammaddegedara, Malintha Amarasinghe

1. Introduction

When following the guideline of this document the resulting API(Application Programming
Interface) will reach Level 1 of the Richardson Maturity Model ([1], [4]). That means a
resource model has been provided, use of proper HTTP(S) methods has been made, use
of appropriate HTTP headers have been identified, and HTTP status codes are used in
responses.

The top level - Level 3 - of the Richardson Maturity Model will not be reached. This
third level assumes to make use of hypermedia controls: such controls allow REST
(Representational State Transfer)servers to inform REST clients about the APIs that may
be invoked in the current state of the application. While this is promising in terms of, for
example, maintainability (e.g. APIs may be changed without clients having to understand
these changes), no best practices have been established yet to deal with this.

2. Overall Approach

The following major steps should be followed to create a RESTful (Level 2) API (see Figure 1).

rest-api-design-figure-01

Figure 01: REST API Design Approach

First, a model of the data to be manipulated by the API is created. From this data model the
resources of the API will be determined; typically, there is no one-to-one correspondence
between data model elements and resources of the API, especially because new kinds of
resources will typically be derived. For each of the resources the representations supported
by the APIs have to be determined. Next, these resources must be named properly by
means of URIs (Uniform Resource Identifiers). For each of the resources the HTTP methods
used to perform the required application functions have to be decided; this includes the use
of applicable HTTP headers. Special behavior required by the application (e.g. concurrency
control, long running requests) has to be decided. Finally, potential error situations have to
be identified and corresponding error messages have to be designed.

Note: Although Figure 1 sketches the approach as a sequential process, following it stepby-step is not always needed. For example,

  • The data model may already be known. In this case, the first step will be omitted.
  • The resource model has already been decided. In this case, the second step may
    be left out. But in case the data model is not precisely specified, it may be worth to
    perform step 1.
  • Your API is very straight-forward, e.g. you don't expect concurrent updates of your
    resources, or none of your APIs kick-off long-running actions. Then you will leave out
    the step to "determine special behavior".

Each individual step of the overall approach will be detailed in the next sections.

3. Data Model

The data model behind an API can be specified by any conceptual data modeling language
like the entity-relationship Model or UML Class Diagrams. In what follows we assume the
use of the entity-Relationship Model.

The main purpose of the data model behind an API is to specify the properties of the
resources manipulated by an API in an abstract (i.e. implementation independent) manner.
By specifying the attributes of the entity types of the data model no early decision
is made about the format and media type in which instances of the entity types (aka
representations in REST) are exchanged - this decision will be made later, and it can be
changed during the lifetime of an API. Thus, it results in more flexibility in the development
process.

rest-api-design-figure-02

Figure 02: Sample Data Model

Figure 2 depicts a sample data model that will be used in the following sections to give examples on how to apply the given guidelines.

The data model is an important source to determine an appropriate resource model.
However, the way clients interact with the API is a significant influencer of the resource
model derived from the data model ("clients win over data"). The domain model drives the
implementation of the API, while the resource model is driven by client interactions. But
typically, the resource model will "follow the data model".

4. Resource Model

The resource model specifies the resources that are processed by the API. Several kinds of
resources will be derived from both the data model as well as the corresponding processing
requirements.

4.1 Atomic Resources

The most basic decision to be made for deriving resources is identifying entities of the data model that are exchanged as a whole via the API. Such entities become atomic resources.

For example, based on the sample data model in Figure 2 the Customer entity will become
such an atomic entity. This is because details about a customer like his address, payment
information etc. will be accessed in several scenarios supported by the API.

4.2 Collection Resources

The next decision to be made is whether atomic resources of the same type are needed to be grouped into a set. Such bundles become collection resources.

For example, products will become a collection resource because the application supports
a catalogue that allows browsing through (subsets of) all products available. Note, that
there is no products entity type in the data model. Because the application requires
such a collection resource we derive it from the data model and give it a new name that
corresponds to the plural of the name of the grouped entity type.

As another example, items will become a collection resource that represent all items
contained in a shopping cart of a certain customer. This collection will be scoped, i.e.
only the items in a specific shopping cart are of interest but not the set of all items in all
shopping carts (see section 5.6 for more details on scoping).

Finally, whenever the API supports the creation of an instance of one of the entity types of
the data model, this entity type results in a corresponding collection resource. For example,
a new customer may register with the application resulting in a new instance of the
Customer entity type. Thus, Customers will become a collection resource.

4.3 Composite Resources

Sometimes, instances of groups of different entity types are manipulated as a whole
because these instances are perceived as aggregates, e.g. they are typically collectively
retrieved or deleted. Such groups become composite resources.

For example, a Shopping Cart is a composite resource because it is often retrieved or
deleted as a whole, i.e. with all of its encompassed items.

4.4 Controller Resources

Controller resources are used when multiple resources have to be manipulated in a single
API call in order to maintain data consistency. If integrity rules between resources must
be obeyed, a client would have to understand these rules like the order in which resources
are to be manipulated. By providing a controller resource to manipulate these resource in
a single API call relieves the client from having to understand these rules - a significant
contribution to loose coupling.

For example, deleting each individual item of a shopping cart one after the other may result
in consistency problems in case an error occurs after having deleted only the first few items
while others are still left in the shopping cart: a customer requesting the shopping cart just
at this point in time of failure will realize a "broken" shopping cart.

Another example is the update of two account resources to realize a funds transfer - the
classical motivation for ACID (Atomicity, Consistency, Isolation, Durability) transactions.
Each of these two accounts is an atomic resource, i.e. controller resources are different from
composite resources.

4.5 Processing Function Resources

Processing function resources (aka computing resources) provide access to functions
that either process particular resources, or that perform certain resource independent
computations. In practice, processing function resources are often used for predefined
partial updates of a resource.

For example, changing the status of a resource like the price of a product, or getting the official exchange rate between two currencies can be realized by means of a processing function resource.

Note: Partial updates are addressed by the HTTP PATCH method [7]. The problem with PATCH is two-fold:

  1. The PATCH method is not (yet) supported by all web servers. Of course, this problem may go away.
  2. The syntax and semantics for each use of the PATH method must be crisply defined:

The resource enclosed in the body of a PATCH method is an instruction document,
i.e. a set of instructions precisely describing what has to be updated and how, and all
of these instructions must be performed atomically. Especially, the media type of this
instruction document is typically different from the media type of the resource that is
to be modified by the PATCH. The instruction document may be perceived as a sort of
transaction on the resource to be manipulated.

This results in broad exploitation of processing function resources for realizing partial updates.

4.6 Interpreting Relationships

One of the basic problems of deriving a resource model from a data model is in interpreting
the relationships between entity types of the data model.

If manipulating instances of a certain entity type does not require the traversal of its
associated relationships, then such an entity type is a candidate for an atomic resource: if
the instances have to be available via the API, the entity type is transformed into an atomic
resource in a one-one correspondence.

Collection resources may be subject to an interpretation of its associated relationship
types, i.e. collections may only make sense as children of other resources (so-called scoped
collections - see 5.6).

For example: The Products collection resource is not scoped, i.e. this collection is a first
class resource of the sample resource model. In contrast to this, the Items collection
resource in fact is scoped: the collection of all items in all shopping carts is typically not of
interest at all, but all items within a certain shopping cart is of interest. Thus, collections of
Items dependent on a certain shopping cart is a collection resource (see section 5.6 how to
denote such scoped collections).

If the APIhas to support the direct creation of instances of an entity type, this entity type
results in a collection resource. This is because in the REST paradigm a collection resource
is a factory for its members (see section 7.3). The entity type itself is the basis for atomic
resources that are the members of the collection resource.

Processing function resources as well as controller resources result from functional
requirements: they are typically not immediately derived from the data model but from
update requirements or from requirements to derive information that may not even be
related to some other resources.

5. Resource URIs

The complete URI of an API complies to the following structure:

{scheme}://{host}/{base-path}/{path}[?{query-string}]1,2

The next sections will explain the elements of this structure.

5.1 Proper Naming

Proper naming of resources is key for an API to be easily understandable by clients. There are a few rules that should be followed ([2], [3], [5]):

  • Atomic resources, collection resources and composite resources should be named as nouns because they represent "things", not "actions" (actions would lean more towards verbs as names of resources).
  • Processing function resources and controller resources should be named as verbs because they in fact represent "actions".
  • Processing function resources and controller resources should not be sub-resources of individual other resources.
    • They should not be named by means of a URI template (see 5.6).
    • Individual resources become parameters.
  • Lower case characters should be used in names only because the rules about which URI element names are case sensitive and which are not may cause confusion.
  • If multiple words are used to name a resource, these words should be separated by dashes (i.e. "-").
    • Especially, no underscore (i.e. "_") should be used: when names are rendered in browsers, they will be interpreted as links, i.e. shown underlined, and, thus, the
      underscore will be difficult to read.
    • Similarly, camel case or other programming language related naming should be avoided.
  • Singular nouns should be used for naming atomic resources.
  • Names of collections should be "pluralized", i.e. named by the plural noun of the
    grouped concept (atomic resource or composite resource).
  • Use forward slashes (i.e. "/") to specify hierarchical relations between resources. A
    forward slash will separate the names of the hierarchically structured resources. The
    parent name will immediately precede the name of its immediate children.

5.2 Schemes

A scheme denotes the transport protocol supported by the API. Typically, WSO2 APIs will
be all accessible over HTTPS, some APIs may support HTTP, and some may support both
schemes.

5.3 Host

The host part of the API specifies the domain of the API. For WSO2 hosted APIs, this value
is apis.wso2.com. When hosted by or for customers this will be substituted by a customerspecific
string.

5.4 Base Path

The base-path of an API follows the structure

/{feature-code}/[ {sub-code}/ ]/{version}

Thus, each base path consists of a feature-code structure indicating the feature for which
this API is for. An optional sub-code structure may be used for features containing logically
independent collections of functionalities. For example, the feature-code may be "apim" for
API manager, in which case no sub-code is used, and the version may be v1.0 (see section
5.5 for details on versioning). For Enterprise Store, the feature-code may be "es", and the
independent Publisher functionality may get the "publisher" sub-code assigned.

The base path will be the same for all APIs of certain features or logically independent
collections of feature functionalities, respectively. This base path will precede each proper
resource name of the API, i.e. the path element of the API's URI. Note, that a path is often
encoded as a URI template (see section 5.6).

5.5 Versioning

The version of an API is specified as part of its URI. This version is specified as a pair of
integers (separated by a dot) referred to as the major and the minor number of the version,
preceded by the lower case letter "v". E.g. a valid version string in the base path would be
v2.1 indicating the first minor version of the second major version of the corresponding API.

Using this versioning scheme is referred to as semantic versioning [6]. In general, a version
number following the semantic versioning concept has the structure major.minor.patch and
the significance in terms of client impact is increasing from left to right:

  • An incremented patch number means that the underlying modification to the API
    cannot even be noticed by a client - thus, the patch number is omitted from the
    version string. Only the internal implementation of the API has been changed while
    the signature of the API is unchanged. From the perspective of the API developer, a
    new patch number indicates a bug fix, a minor internal modification, etc.
  • An incremented minor number indicates that new features have been added to
    the API, but this addition must be backward compatible: the client can use the old
    API without failing. For example, the API may add new optional parameters or a
    completely new request.
  • An incremented major number signals changes that are not backward compatible: for
    example, new mandatory parameters have been added, former parameters have been
    dropped, or complete former requests are no longer available.

It is best practice to support the current major version as well as at least one major version
back. In case new versions are released frequently (e.g. every few months) more major
versions back have to be supported. Otherwise, clients will break (too fast).

When a client is using an API's URI with a version number no longer supported, the server
has to respond with the following response message that especially contains a Location
header field with the URI of the latest version of the API:

HTTP/1.1 301 Moved Permanently

Location:

Note: there is a lot of debate on the subject of how to specify versions, and a couple
alternative approaches to this area are presented. The discussion spans the whole spectrum
from what the pure REST style considers a resource to what the big players providing Web
APIs offer. What is recommended here is a pragmatic guideline.

5.6 URI Templates

A URI template is an element which contains strings in curly brackets [8]. These strings are
variables that must be substituted by values when such a URI template is used by a client.

For example, when using the URI template

/shopping-carts/{shopping-cart-id}/{item-id}/product

"shopping-cart-id" and "item-id" are variables. These variable must be substituted when an
API using this URI template in its path should be used.

A typical use of URI templates is in collection resources. An individual member of a
collection will be identified by a unique value that will become the variable in the template
immediately following the name of the corresponding collection resource. For example:

/products/{product-id}

Note the difference between this URI template and the one before. This URI template
denotes a collection that is immediately derived from an entity type of the data model; all
instances of this entity type should be accessible from the API, and they are grouped into
the collection for that purpose.

The URI template before is different in the sense that it has been abstracted from a relation
type of the data model, namely the relation between the Shopping Cart entity type and
the Item entity type. Since there is no need to access the set of all instances of the Item
entity type, no dedicated items collection type is part of the resource model. Instead, only
items associated with a certain shopping cart (identified by its shopping-cart-id) should be
accessible by the API, i.e. this set of items is scoped by the corresponding shopping cart:
the collection of items is called a scoped collection.

5.7 Query String

By definition, an optional query string (if specified) is a part of the URI contributing to the
unique identification of a resource. i.e. the URI without the query string and the URI with
the query string identify different resources (a fact that is often ignored).

However, it is best practice to not use fields of the query string as identifier components.
In this sense, a query string provides parameters to control the execution of the API
processing the resource identified by the structure preceding the "?" symbol:

{scheme}://{host}/{base-path}/{path}?{query-string}

The query string consists of a sequence of name/value pairs that are separated by an "&".
The name-string and the value-string are separated by a "=".

In practice, URIs are limited in size. Even worse, the maximum size supported by products
differ. As a consequence, parameters of large query strings have to be moved into the
message body of the corresponding requests. Note, that this will only work for requests
that allow a message body (especially POST, but not GET).

6. Representation Specification

A format in which instances of the entity types of the data model are exchanged is referred
to as a representation of such an instance. A representation is some sort of the shape of a
resource, not the resource itself. In this sense, a representation is some sort of view onto the
resource.

The information content of an atomic resource or a composite resource is immediately
defined by the data model underlying an API. The values of the attributes and - if
appropriate - the identifiers of associated resources make up the information content of an
atomic resource. Similarly, the aggregate of the information contents of the resources of a
composite resource is the information content of the composite resource.

A data structure must be decided for this information content. In addition, one or more
renderings of this data structure must be decided. For example, a certain data structure
may be rendered as a JSON (JavaScript Object Notation) document or an XML (eXtensible
Markup Language) document. Typically, renderings of data structures are specified
by means of MIME types. A specific rendering of a data structure is referred to as the
representation of the resource, i.e. a representation has a MIME (Multipurpose Internal Mail
Extensions) type. Keep in mind, that this MIME type does not indicate the data structure
of the information exchanged between the client and the API implementation but only the
rendering.

Note: In most practical situations, a single representation (e.g. JSON) for all entities
exchanged via an API suffice.

7. HTTP Methods Used

Manipulation of resources in the REST style is done by create, retrieve, update, and delete
operations (so-called CRUD operations), that map to the HTTP methods POST, GET, PUT,
and DELETE.

A request that can be used without producing any side-effect is called a safe request. A
request that can be used multiple times and that is always producing the same effect as the
first invocation is called idempotent.

7.1 Get

GET is in HTTP as well as in the REST style specified as a safe and idempotent request.
Thus, an API using the GET method must not produce any side-effects. Retrieving an
atomic resource (4.1) or a composite resource (4.3) is done by performing a GET on the URI
identifying the resource to be retrieved.

Retrieving a (subset of) resources of a certain type is done by performing a GET on the URI
of the collection resource (4.2) of that type, and specifying a filter condition (10.2).

7.2 Put

PUT substitutes the resource identified by the URI. Thus, the body of the corresponding
PUT message provides the modified but complete representation of a resource that will
completely substitute the existing resource: parts of the resource that have not changed
must be included in the modified resource of the message body. Especially, a PUT request
must not be used for a partial update. As a consequence, PUT is an idempotent request
(but not safe).

Partial updates, i.e. updates that modify only selective pieces of an existing resource have
to be realized by means of corresponding processing function resources (see section 7.3).

Note: PUT may be used to create a new resource, but this is not recommended. The reason
is that in this case, the client is in charge of creating such a globally unique URI identifying
the newly created resource - and ensuring uniqueness of identified is a difficult task. In
contrast, using POST to create new resources relieves the client from creating unique
identifiers because the server will create the URI and return it to the client (see section 7.3)

7.3 Post

POST is neither safe nor idempotent. The main usages of POST are the creation of new
resource, and the initiation of functions, i.e. to interact with processing function resources
(4.5) as well as controller resources (4.4).

In order to create a new resource, a POST request is used with the URI of the collection
resource to which the new resource should be added. If the POST is processed successful,
the response message will especially include a Location header that will have the newly
created URI of the added resource as value. Also, it is a good practice to return in the
response message a Last-Modified header containing the time the resource has been
created, as well as the ETag (Entity Tag) header containing the entity tag of the new
resource.

It is often appropriate that the client checks the correctness of the created resource. For
this purpose, the response message body contains the resource as it would be returned by
retrieving it from the URI of the Location header. In this case, the response message also
includes a Content-Location header repeating the URI of the newly created resource.

7.4 Delete

A resource is deleted by means of the DELETE request on the URI of the resource. Once
a DELETE request returned successfully with a "200 OK" response, following DELETE
requests on the same URI will result in a "404 Not Found" response because there is no
resource available with the URI of the deleted resource.

Note: By definition, DELETE is an idempotent request, which has the following curious
theoretical implication. Responding with "200 OK" to the first DELETE and with "404 Not
Found" for any further DELETE on the same URI is not quite RESTful because the responses
of the first and all further requests are different; thus, the request is not idempotent. In
order to fully comply to the REST style, a server would have to maintain all URIs of deleted
resources in order to always respond with "200 OK", i.e. with the same response. This is
considered to much of an effort for nearly no gain, i.e. this is not implemented in practice.

8. Headers

HTTP headers provide the vehicle for many non-functional properties of REST APIs. The
following list of HTTP headers are used in most APIs.

8.1 Request Headers

Accept

This is the list of content types acceptable for the client.

Authorization

The credentials of the client for authentication by the server.

Content-Type

This is the MIME-type of the message body of the PUT or POST request.

If-Match

Used to avoid concurrency conflicts: if the client-passed entity tag is identical to the entity tag of the resource at the server-side, the request if performed.

If-Modified-Since

Used to avoid retrieving data that has been cached by the client. If the client-provided timestamp is identical to the time the entity has been modified last at the server side no message body is returned.

If-None-Match

Used to avoid retrieving data that has been cached by the client. If the client-provided entity tag is identical to the entity tag of the resource at the server side no message body is returned.

If-Unmodified-Since

Used to avoid concurrency conflicts. if the client-passed last-modified time stamp is identical to the time the resource has been changed last at the server-side, the request is performed.

8.2 Response Headers

Content-Location

The URL of the message body. For example, the URL of the resource describing the status of a long running request (see 10.6).

Content-Type

The MIME-type of the message body.

ETag

A "fingerprint" of the resource as currently available at the server, often a digest of the resource.

Last-Modified

The timestamp when the resource has been modified the last time at the server.

Location

The URL of a newly created resource.

WWW-Authenticate

An indication of the authentication scheme to be used to access the resource.

9. Status Codes

HTTP status codes [9] are returned by response messages and provide key information to clients about the status of a request. The following status codes are used in many APIs.

200 OK

The request has been performed successfully. If the request was a GET, the requested
resource is returned in the message body. If the request was a POST, the result of the
requested action is described by the message body, or it is contained in the message body.

201 Created

The request has been performed successfully. The URL of the newly created entity is
contained in the Location header of the response. An ETag header should be returned with
the current entity tag of the resource just created. The response may also contain an entity
corresponding to the created resource.

202 Accepted

The processing of the request has started but will take some time (see 10.6). The success
of the processing is not guaranteed and should be checked by the client. The body of the
response message should provide information about the current state of the processing, as
well as information about where the client can request updated status information at a later
point in time; typically, the Content-Location header of the response contains a URL where
this status information can be retrieved via GET.

303 See Other

The response of the request is available at a different URL; this URL is given as value of the
Location header of the response message. Typically, this status code is returned after the
processing of a long running request is completed and the client retrieves the status of the
long running request (see 10.6).

304 Not Modified

The requesting client has already received the latest version of the requested resource.
Thus, the body of the response message must be empty. This status code is returned as a
result of a conditional GET (see 10.4), and the the specified conditions (i.e. If-Non-Match, IfModified-Since)
are not met.

400 Bad Request

The request is invalid. For example, syntax errors in expressions passed with the request are
found, values are out of range, required data is missing etc.

401 Unauthorized

The request requires client authorization or the passed credentials are not accepted. The
response must include a WWW-Authenticate header. The request may be repeated by the
client including proper credentials in the Authorization headers.

403 Forbidden

The server understood the request but refused to perform it. For example, the request must
be conditional but no condition has been specified.

404 Not Found

The requested entity does not exist.

406 Not Acceptable

The requested media type is not supported. For example, a GET request wants to retrieve
an entity in a media type (specified as value of the Accept header of the request) not
supported by the server.

412 Precondition Failed

The request has not been performed because one of the preconditions has not been met. This status code is returned when the request was conditional (see 10.5) and one of the conditions specified (i.e. If-Match, If-Unmodified-Since) is not met.

415 Unsupported Media Type

The entity passed by the request was in a format that is not supported. For example, a
PUT request passed an entity in its body, and this entity was in a format or media type,
respectively, that is not understood by the server.

Note: 5xx status codes denote severe errors at the server side or the network, or denote
not implemented functions, etc. Such errors are very generic, i.e. there is no need to
document them explicitly for an API.

10. Special Behavior

Except for very simple APIs, a REST API has to offer features that allows to cope with
advanced situations like large result sets, concurrent updates, or long running requests. The
following describes best practices to deal with some of those special situations.

Note: It is a good practice for an API to support at least queries (see 10.2) and pagination
(see 10.3). For example, this will allow to support push-down of filtering, etc. from an API
orchestration (a more and more important API technology [10]) to the individual APIs as
optimization of response time, bandwidth usage, etc

10.1 Content Negotiation

The REST style clearly distinguishes between a resource itself (i.e. as an abstract entity) and
its different possible representations. Such a representation is a rendering of the resource's
information content in the format of a certain media type. Which of the representation of a
resource is returned as content of the message body is negotiated between a client and the
resource provider (a.k.a. content negotiation).

Server-driven content negotiation assumes that a server processing a request determines
the representation best suited to the requesting client. The server is dependent on the
requesting client to specify information about its processing capabilities or requirements.
The latter is done by means of header fields of the request message like Accept, AcceptEncoding
etc.

In client-driven content negotiation the server detects that it has more than one
representation of a resource available that may serve the client's needs. The server
responds to the request with a "300 Multiple Choices" message that carries information
about the representations available, and the client finally selects one of these
representations and explicitly requests it in a following request.

Server-driven content negotiation has the advantage of avoiding a second request to be
made by the client, but has the disadvantage of potentially responding a representation
to the client that is not ideally suited. Client-driven content negotiation has the advantage
that the client gets the representation that is best suited for it, but the disadvantage of two
round-trips.

In the following request, the client specifies that it is able to process JSON, XML as
well as plain text, but that it prefers JSON. The preferences in media types is specified
by weighting a media type by a quality value q. The server will determine which of the
representations it has available and will return the one with the highest quality value.

Example:
GET /products/27182

Accept: application/json;q=0.9, application/xml;q=0.6, text/plain;q=0.1


In case the server does not have a client specified representation, it will respond with a
message returning a corresponding status code:

HTTP/1.1 406 Not acceptable

Note: Even in case an API document supports only one certain media type, a client may
pass a request that contains an Accept header with a different media type. In this case, the
API implementation must return the "406 Not acceptable"message.

10.2 Queries

A query on a collection resource consists of three artifacts: (i) a mandatory filter condition,
(ii) an optional sort expression, and (iii) an optional projection. First, a list of attributes from
the entity type that can be used in either of these artifacts on which the collection is based
on has to be distinguished.

A filter condition is a boolean expression in these attributes. The spectrum of filter
conditions is from simple (supporting to specify a single attribute name with a value
being compared for equality) to complex (arbitrary conditions and arbitrary comparison
operators).

Example of a complex query:
filter=((price > 1000 AND status = on-stock) OR (price 200 AND NOT(status = on-stock)))

A sort expression consists of a list of attribute names together with the indication for each
attribute name whether the result is to be sorted ascending or descending with respect to
the corresponding attribute. The order in which the result is sorted is implied by the order
of the list of attribute names.

Example:
sort=(price ASC, delivery-date DESC)

A list of attribute names that have to be used to create each item in the result set is called a
projection. Each specified attribute name is used to extract the corresponding information
from each resource qualified and compile a corresponding item in the result set.

Example:
projection=price,color,status

There are two ways to enable queries on collections. First, a query is part of the query
string of the URI used by a GET. Second, the query is passed in the body of a POST request
of a special processing function resource.

An example of specifying a query as query string of the URI is:
GET /products?status=on-stock&sortAsc=price&projection=price,color,status HTTP/1.1

Specifying a query as part of a query string concatenated to the URI of the collection
resource that is enquired is the preferred way for queries. This is because a GET is used
with this URI, clearly expressing the semantics of the request namely retrieving a subset of
the collection. However, in practice URIs have a maximum length depending on the browser
or Web server used. When queries may become thus complex that the maximum length
may be exceeded, the use of a POST request that contains the (complex) query in the
request body is enforced. For this purpose, a separate processing function resource is to be
realized:

POST /product-search HTTP/1.1


filter=((price > 1000 AND status = on-stock) OR (price 200 AND NOT(status = on-stock)))
sort=(price ASC, delivery-date DESC)
projection=price,color,status

10.3 Pagination

When a large collection resource (or subsets of it) is to be retrieved, it is often convenient
to retrieve the result set in smaller chunks: for example, the latency of the request is
reduced, clients can predict the amount of data to be dealt with, etc.

For this purpose, the retrieval request specifies a query string containing an "offset" field
as well as a "limit" field; the offset is the position number of a qualified resource where the
retrieval should start, and the limit is the maximum number of resource to be returned.

The response message with the subset of the qualified resource returned should specify the
total number of all qualified resources ("count" field), a link to the next chunk of qualified
resources ("next" field), as well as a link to the previous chunk of qualified resources
("previous" field). The actual format of how these fields as well as the set of resources is
returned, is application specific. The following example is intended to show the principle
only.

Example:
GET /products?offset=42&limit=3

Response:
HTTP/1.1 200 OK


count=119

next={link-to-next-subset}

previous={link-to-previous-subset}

P42-details

P43-details

P44-details

10.4 Client-Side Caching

A client may cache resources as well as header fields of resources to reduce transfer of
data that has not been changed since its last retrieval. For this purpose, a client uses a
conditional request to retrieve data. Such a conditional request specifies the If-None-Match
or the If-Modified-Since headers in the request.

The value of the If-None-Match header is the value of the ETag header of the resource as
retrieved last time by the requesting client. The value of the If-Modified-Since header is the
value of the Last-Modified header of the resource as retrieved last time by the requesting
client. When performing the conditional request, the API implementation has to compare
the value(s) passed by the client in the request with the corresponding values of the
current resource as stored by the server (note3 that If-None-Match takes precedence over
If-Modified-Since). If the values have not changed, the resource is not returned (response
code "304 Not Modified"); otherwise, the resource is returned.

Example:

GET /products/31415

If-None-Match: "42049dcaf450987cffd"

If-Modified-Since: Thu, 7 Jan 2016 17:41 CET

Response:

HTTP/1.1 304 Not Modified

10.5 Concurrency Control

Multiple clients interacting with the same resource may cause concurrency conflicts like
lost updates. Pessimistic concurrency control mechanisms avoid conflicts in advance by
locking resources. This is a good choice, if the probability for conflicts is high. Optimistic
concurrency control avoids conflicts by detecting conflicts and signaling them to clients
that may retry their requests. This is a good choice, if the probability for conflicts is low.

Many concurrent interactions in REST-based APIs can be handled by optimistic concurrency
control. For this purpose, requests are sent as conditional requests. A conditional request
specifies the If-Match or If-Unmodified-Since header in the request. The value of the IfMatch
header is the value of the ETag header of the resource as retrieved last time by the
requesting client. The value of the If-Unmodified-Since header is the value of the LastModified
header of the resource as retrieved last time by the requesting client. When
performing the conditional request, the API implementation has to compare the value(s)
passed by the client in the request with the corresponding values of the current resource as
stored by the origin server (note that If-Match takes precedence over If-Unmodified-Since).
If the values have been changed, the request is rejected (response code "412 Precondition
Failed"); otherwise, the request is executed.

Example:

PUT /products/31415

If-Match: "42049dcaf450987cffd"

If-Unmodified-Since: Thu, 7 Jan 2016 17:41 CET

Response:

HTTP/1.1 412 Precondition Failed

In order to send conditional requests, the requesting client either has to cache these values
after having retrieved the resource before, or has to retrieve these values by a GET on the
resource that is performed before the conditional request is made. The disadvantage of the
latter is that a second request has to be made.

Example:

GET /products/31415
...

Response:

HTTP/1.1 200 OK

ETag: "4562aae7732a56"

Last-Modified: Wed, 6 Jan 2016 11:13 CET

...

Even with PATCH, special care must be taken with respect to lost updates: if concurrent
PATCHes are to be supported, concurrency control has to be implemented as a conditional
request.

A similar pattern may be used to realize partial updates without using processing function
resources (section 4.5): a client has to GET the resource to be updated, apply the partial
updates locally, and then send a conditional PUT with the complete resource to the server.

10.6 Long Running Requests

Requests may take too long to return the response synchronously, i.e. such requests have
to be processed asynchronously. For example, a request like applying for a credit (see next
example) may kick-off a workflow involving human beings that will take some time. In this
case, the API will accept the request and return both, the URI of a so-called task resource
as well as the task resource itself in the response body. The task resource is a document
like in the following example, that contains the actual status of the long running request.
The URI of the task resource is passed in the Content-Location header field of the response.
This URI can be used by the client to poll for the processing state of the long running task
later on.

Example:

POST /apply-for-credit HTTP/1.1
...

Response:

HTTP/1.1 202 Accepted

Content-Type: application/xml

Content-Location: https://www.shark-credits.com/apply/tasks/1


status >

  state > running /state>

  link rel="self" href=".../tasks/1"/ >

  estimatedCompletion> 2020-04-01 /estimatedCompletion>

/status>

The response message specifies, by the "202 Accepted" status code, that the request is
accepted but will be processed asynchronously. The task resource says that the request is
running and gives an estimated completion time, amongst other appropriate information
(note, that there is no standardized format of a task resource). In succeeding requests, the
client will retrieve the actual task resource via the URI value of the Content-Location header
field:

GET /apply/tasks/1 HTTP/1.1

Host: www.shark-credits.com

...

The sample response succeeds successfully ("200 OK"), which means that the retrieval of
the task resource was successful - note especially that this does not indicate that the long
running request completed successfully:


HTTP/1.1 200 OK

Content-Type: application/xml

Content-Location: https://www.shark-credits.com/apply/tasks/1


status >

  state > running /state>

  link rel="self" href=".../tasks/1"/ >

  estimatedCompletion> 2020-10-10 /estimatedCompletion>

/status>

After some time, the long running request will have completed, i.e. the GET request above
will receive the following response:


HTTP/1.1 303 See Other

Content-Type: application/xml

Location: https://www.shark-credits.com/apply-for-credit

Content-Location: https://www.shark-credits.com/apply/tasks/1


status >

  state > ready /state>

  link rel="self" href=".../tasks/1"/ >

  message> Image processed & stored /message>

/status>

The status code "303 See Other" specifies that a new resource has been created with the
URI with the value of the Location header field. This is the URI of the result of the long
running request. The task resource now reports in its state field that the long request
succeeded successfully.

The long running request may fail. In this case, the retrieval of the task resource will
succeed with a "200 OK" status code and the task resource's status will report that the long
running request completed but was not successfully. Thus, it must be kept in mind, that the
status code of the response of the retrieval of the task resource is not related to the status
of the long running request at all.


HTTP/1.1 200 OK

Content-Type: application/xml

Content-Location: https://www.shark-credits.com/apply/tasks/1


< status >

 < state > FAILED </state>

 < link rel="self" href=".../tasks/1"/ >

 < estimatedCompletion> 2020-10-10 </estimatedCompletion>

</status>

11. Reporting Errors

When a 4xx status code is returned, a client error has been detected. In this case, more
detailed error information should be returned in the body of the response message. The
following error information (in YAML) detailing the client error will help the client to
understand how to fix the request and repeat it successfully.

Error:

 title: Error object returned with 4XX HTTP status code

 required:

&nbsp - code

&nbsp - message

 properties:

&nbsp code:

&nbsp&nbsp type: integer

&nbsp&nbsp format: int64

&nbsp&nbsp description: |

&nbsp&nbsp&nbsp A product-specific error code.

 message:

&nbsp&nbsp type: string

&nbsp&nbsp description: |

&nbsp&nbsp&nbsp A detailed description of the error occured.

 description:

&nbsp&nbsp type: string

&nbsp&nbsp description: |

&nbsp&nbsp&nbsp A short description about the error message.

 moreInfo:

&nbsp&nbsp type: string

&nbsp&nbsp description: |

&nbsp&nbsp&nbsp Preferably a URL with more details about the error.

error:

 type: array

 description: |

&nbsp If more than one error occurred they are listed separately.

&nbsp For example, list out validation errors by each field.

 items:

&nbsp $ref: '#/definitions/ErrorListItem'

If more than one error occurred, each error is reported as an element of the following array that is part of the error object before.

ErrorListItem:

 title: Description of individual errors that may have occurred during a request.

 required:

&nbsp - code

&nbsp - message

 properties:

&nbsp code:

&nbsp&nbsp type: integer

&nbsp&nbsp format: int64

 message:

&nbsp&nbsp type: string

&nbsp&nbsp description: |

&nbsp&nbsp&nbsp Description about the individual error occurred

12. Security

Access to resources of an API are typically secured. These resources should be accessible
based on a properly designed permission model to prevent misuse of APIs. For example,
adding, updating, or deleting permissions to access resources or operations as well as
associated users or roles etc. is needed. Furthermore, access requirements can change, i.e.
the ability to manage these permissions flexibly is required.

The following examples elucidate this:

  • The user who created a shopping cart has the permission to create and delete an
    individual item from the shopping cart.
  • Users need explicit permissions to add new products or update existing product
    information.
  • A different set of users will get permission to delete customers (for example those
    with bad credit standing).

Thus, different resources are protected by different permissions and these permissions may
change. To support this, REST APIs need to support extensible security mechanisms like
HTTP Basic Authentication, OAuth (Open Authentication), or XACML (eXtensible Access
Control Markup Language).

Note: Because of the complexity of XACML, OAuth is the pragmatic way of adding security
to APIs. In what follows, XACML is sketched for completeness reasons only.

12.1 Basis Authentication

HTTP Basic Authentication [11] requires that a request contains an Authorization header
field containing user-id and password credentials in Base64 encoding:

Authorization: Basic Zm9d03wWUdz==

In case the request does not contain the credentials, the server will respond with a 401
status code containing the information about the protected resources ("realm") and the
name of the authentication scheme to be used (i.e. "Basic"):

HTTP/1.1 401 Unauthorized

...

WWW-Authenticate: Basic realm="/product"

...

This information will be used by the client to produce the correct value of the Authorization
header to be passed with the repeated request. Otherwise, the request will fail again.

Note, that HTTP Basic Authentication is unsecure. This is because the credentials are
in clear text, i.e. it is Base64 encoded, which can be immediately translated into clear
text. Because of this, HTTP Basic Authentication is only viable over HTTPS. HTTP Digest
encoding is more secure but is subject to a "man-in-the-middle-attack". By using HTTPS,
this is avoided. Thus, HTTP security mechanisms are only acceptable for non-critical APIs,
for requests that generate more secure access tokens (see next) - or these mechanisms are
used over HTTPS only.

12.2 Open Authorization (OAuth)

The likelihood of possible attacks of HTTP Basic or Digest Authentication is reduced by
using OAuth [12] tokens with the HTTP Bearer authentication mechanism. The request
message contains an Authentication header with the OAuth access token:

Authorization: Bearer mF_9.B5f-4.1JqM,

The string after the Bearer keyword is the credential provided by means of OAuth protocols
and mechanisms. The value of the scope field is a list of strings denoting the resources
intended to be accessed by the request. In case the token provided is not sufficient for the
scopes, the request fails.

Note: The use of OAuth protection of APIs requires a design of scopes along with the
APIs. A set of scopes needs to be designed that protects individual processing function
resources, (subsets of) collection resources, or the ability to create or delete resource, for
example.

While OAuth reduces the likelihood of attacks when used over HTTP, there are several
attacks known to OAuth. Thus, although OAuth has been proposed as a mechanism over
HTTP, it should be used over HTTPS to increase security.

12.3 eXtensible Access Control Markup Language (XACML)

A more fine-grained control of access to resources (i.e. finer than scopes) is supported
by XACML [13], but XACML is considered quite complex in practice. The following sketch
should underpin this.

XACML is an attribute-based access control (ABAC) mechanism, in contrast to role-based
access control (RBAC) mechanisms. As such, access to a resource is determined based on
attributes contained in a request message.

For this purpose, XACML describes an access control policy language, a request/
response language and protocol, and a reference architecture. The policy language is
used to express access control policies (who can do what in which context). The request/
response language supports queries about whether a particular access should be allowed
(requests) and specifies answers to those queries (responses). The reference architecture
proposes deployment of necessary components within an infrastructure to allow efficient
enforcement of policies.

XACML Processing

When using XACML to secure API access, a policy enforcement point (PEP) is assumed. It is
in charge of extracting the required parameters from the API request message. Permissions
are validated by a policy decision point (PDP) based on these parameters. The PEP will
create a XACML request based on the extracted parameters. This request is sent to the PDP
to validate permissions. The result of permission validation is returned by the PDP in the
response message and is based on predefined policies. The response message will contain
one of the following statuses:

Permit - the access is granted.

Deny - the access is not granted.

Indeterminate - an error occurred, or some required data was missing and no decision could be made.

Not Applicable - the request could not be processed.

Implications on API Design

When an API call is received, the PEP must return an HTTP 401 Unauthorized status
code if anything other than Permit results from access validation. In case the request is
authorized (i.e. Permit resulted from access validation), the request will be pass to the API
implementation.

XACML may be used with other authentication or authorization mechanisms such as HTTP
Basic Authorization or OAuth.

Note: The use of XACML for protecting APIs requires (i) an implementation that follows
the architecture above, and (ii) the ability for API administrators to define corresponding
policies.

13. References

  1. https://martinfowler.com/articles/richardsonMaturityModel.html
  2. M. Masse: REST API - Design Rulebook. O'Reilly 2012.
  3. S. Allamaraju: RESTful Web Services Cookbook. O'Reilly 2010.
  4. J. Webber, S. Parastatidis, I. Robinson: REST in Practice. O'Reilly 2010.
  5. https://github.com/tfredrich/RestApiTutorial.com/blob/master/media/RESTful%20Best%20Practices-v1_2.pdf
  6. https://en.wikipedia.org/wiki/Software_versioning
  7. https://tools.ietf.org/html/rfc5789
  8. https://tools.ietf.org/html/rfc6570
  9. https://www.restapitutorial.com/httpstatuscodes.html
  10. https://tools.ietf.org/html/rfc2617
  11. https://tools.ietf.org/html/rfc6749
  12. https://docs.oasis-open.org/xacml/3.0/xacml-3.0-core-spec-cs-01-en.pdf


[1] Strings in curly brackets {} represent variables that must be substituted by API-specific structures.

[2] Strings in square brackets [] represent optional elements.

[3] https://tools.ietf.org/html/rfc7232#section-6

For more details about our solutions or to discuss a specific requirement

x

Interested in similar content?