Skip to main content

Classification scheme for developer-friendly APIs

1       Introduction

Application Programming Interfaces (APIs) are increasingly deployed to facilitate data sharing between applications – often even across organisations. A core tenet of APIs is that they provide a structured method for sharing data, thus ensuring that data sharing processes are scalable and can be automated based on transparent, standardised, well-understood design features.

Whether this endeavour is likely to succeed depends on the quality of the API design – more precisely its developer-friendliness. Thus, interest in API design has risen particularly as the number of online services and data providers is growing.

The WEB offers plenty of evolving guidelines and advice when it comes to API specifications. Their intention is to proliferate the development of user/developer friendly APIs in order to make such services more accessible. The most central and arguably most comprehensive API specification is the OpenAPI-specification1, around which an entire ecosystem of services has grown.

With its Classification Scheme for Developer-Friendly APIs, the Support Centre for Data Sharing (SCDS) presents a condensed API scheme that builds on the OpenAPI specification, but focusses on seven key areas and a reduced set of essential criteria for developer friendly APIs. Where necessary, we have also added criteria that are not included in the OpenAPI specification, but should not be missed from an SCDS point of view.

The classification scheme for developer-friendly APIs is already implemented as part of the SCDS API Friendliness Checker2. You can therefore use this documentation to better understand what precisely is checked by the API Friendliness Checker – and, of course, to improve the quality of your API.

 

2       The schema

2.1      Building on the OpenAPI specification

V3.0 of the OpenAPI Specification3 was chosen as the basis for the SCDS Classification Scheme for Developer-Friendly APIs because it provides the most widely recognised standard for describing APIs adhering to REST criteria. Furthermore, it has been developed with a specific focus on accessibility so that compliant services and applications can be understood by humans and machines - even without access to programming code or documentation. 

Beyond the scope of the OpenAPI specification, we have also added some characteristics that we consider to be important:

  • Using TLS encryption;
  • limiting the length of context URLs to less than 2,000 characters;
  • avoiding plain text responses;
  • offering sorting functions.

The OpenAPI specification provides guidelines on versioning, format, data types and the structure of the YAML file that contains the API specification. These were used as a basis for the development of the SCDS Classification Scheme for Developer-Friendly APIs in seven dimensions: 

  • URL format; 
  • consistent usage of HTTP; 
  • TLS encryption; 
  • versioning; 
  • response values; 
  • pagination, filtering and sorting; 
  • and sub-resources.

 

2.2     Dimension A: URL format

Description
In the API design process, the URL format is an important, probably the most basic decision to be made. URLs should employ a consistent casing style (mostly snake_case) which increases readability for humans. Using plurals for resource names is a convention that is supported by the REST paradigm where endpoints represent actual resources.
Classification criteria
 

Criterion ID

Description

Example of bad practice

Example of good practice

A1

Utilisation of snake_case format for property names

amountToPay 

amount_to_pay

Properties must be names with snake_case and follow this pattern:
^[a-z_][a-z_0-9]*$

A2

Utilisation of snake_case format for query parameters

employeeName

employee_name

snake_case must be the standard, camelCase must not be used.

A3

Utilisation of UPPER_SNAKE_CASE format for enum values

colouroptions

COLOUR_OPTIONS

The use of UPPER_SNAKE_CASE allows to distinguish enum values from other properties or elements.

A4

Utilisation of Hyphenated-Pascal-Case for HTTP header fields

accept-encoding

Accept-Encoding

HTTP headers are case-insensitive by default4 but it is preferred to name them with Hyphenated-Pascal-Case.

A5

Pluralisation of resource names

employee

employees

Since resources mainly include more than one instance, they must always be named with the plural form.

A6

Pluralisation of array names

translation

translations

Arrays contain multiple values and must therefore be named with the plural form.

A7

Avoidance of verbs in URLs

https://api.eu/

users/345/delete

https://api.eu/

users/345/deletion

Since an API describes resources, nouns must be used here. Verbs are reserved for the HTTP methods.

A8

Utilisation of URL-friendly resource identifiers

https://api.eu/

users/ #87

https://api.eu/

users/jane_miller1

Resource names must follow this pattern: [a-zA-Z0-9:._\-/]*

A9

Avoidance of trailing slashes

/users/

/users

Trailing slashes at the end of a URL must not have a specific meaning, resource paths must deliver the same result with and without a trailing slash. Therefore, trailing slashes must be omitted in the API description.

A10

Context URL length under 2,000 characters5

/resource/{id}
/sub_resource/{id} /sub_sub_resource/
{id}/.../...


[Total length of context URL longer than 2,000 characters.]

/resource/{id}
/sub_resource/{id}


[Total length of context URL shorter than 2,000 characters.]

URLs (e.g. http://url.com/sub/resource) consist of two parts: A server URL (yellow) and a context URL (turquoise). APIs must ensure that a context URL never exceeds 2,000 characters, as search engines may have limitations for URL length that can otherwise lead to unwanted behaviour.

 

2.3      Dimension B: using HTTP consistently

Description
In order to provide API users with basic and widely understandable feedback, the API should use HTTP status codes consistently. Especially for error handling and other exceptions, using status codes helps avoid ambiguities and, ultimately, saves significant development time.
Classification criteria
 

Criterion ID

Description

Example of bad practice

Example of good practice

B1

Utilisation of correct HTTP methods

/users:

get:

  summary:

   Add a new   

    user

/users:

 get:

  summary: Get    

    all users

HTTP methods must be used as specified in the Hypertext Transfer Protocol.6

B2

Utilisation of standard HTTP status codes

responses:

 ‘900’:

  description:

   Fantasy code

responses:

 200’:

  description:

   OK

HTTP status codes must be used as specified in the Hypertext Transfer Protocol7. No new status codes shall be added.

B3

Utilisation of most specific HTTP status code

/users:

 post:

  summary:

   Add a new   

    user

  responses:

   ‘200:

   description:

    OK

/users:

 post:

  summary:

   Add a new   

    user

  responses:

   201:

   description:

    Created

The most suitable status code must be used when conveying information about the status of a request or an error response.

B4

Specification of success and error responses

/user/{user_id}:

 get:

  summary:

   Returns a  

   user by ID.

  responses:

   ‘200:

   description:

    A user

    object.

  

/user/{user_id}:

 get:

  summary:

   Returns a user  

     by ID.

  responses:

   200:

   description:

    A user object.

   404:

   description:

    A user with

    the specified

    ID was not  

    found.

The API description must define all success and service-specific error responses; errors that are outside of the control of the party providing the service do not need to be documented.

 

2.4      Dimension C: TLS encryption

Description
Developers should be able to rely on some level of security, ensuring that messages between interfaces cannot be intercepted. SSL still plays a significant role in securing APIs, but today it is  considered the lowest security standard. We recommend using TLS encryption because it is more performant and secure – especially in modern web browsers.
Classification criteria
 

Criterion ID

Description

Example of bad practice

Example of good practice

C1

Utilisation of TLS Encryption8

[Not using TLS encryption from OpenAPI v3.1.0 onwards.]

openapi: ‘3.1.0’
[...]
security:
 - api_security: []
[...]
components:
 [...]
 securitySchemes:
  api_security:
   type: mutualTLS
   description: TLS
    encryption for
    API security
   name: apisecurity

TLS encryption must be used throughout the API. This is only mandatory for APIs implementing an OpenAPI version from 3.1.0 onwards.

 

2.5      Dimension D: versioning

Description
APIs should use a consistent versioning scheme to help users understand and more easily identify new and legacy API features. Consistent versioning is also a standard approach to allow API developers to rapidly and safely publish new features. We recommend the SemVer specification9 because it offers clear guidelines for API changes, following the MAJOR.MINOR.PATCH format.
Classification criteria
 

Criterion ID

Description

Example of bad practice

Example of good practice

D1

Utilisation of semantic versioning

info:

 title: API

 description:    

   Example API

 version: 3

info:

 title: API

 description:    

   Example API

 version: 3.1.1

The MAJOR.MINOR.PATCH format must be used to specify a version in an API

 

2.6      Dimension E: response values

Description
Only data types specified in the OpenAPI specification must be used in any response values. This ensures that users can successfully adapt to the response and are not thrown off by non-standard data types. In particular, standardised values shall be used for property formats, dates and times, country, language, and currency codes.
Classification criteria
 

Criterion ID

Description

Example of bad practice

Example of good practice

E1

Utilisation of standardised property formats

properties:

 id:

  type: string

  format:   

   my_own_format

properties:

  id:

   type: integer

   format: int64

Standardised property formats must be used as they are specified in the OpenAPI Specification10.

E2

Utilisation of standard date and time formats

“21.09.2020”

“2020-09-21T13:45:00.252Z”

Date and time must be used as defined in RFC 333911 for JSON format and RFC 723112 for HTTP header info.

E3

Utilisation of standards for country, language and currency codes

“UK”, “UKP”

“GB”, “GBP”

Country, language and currency codes must be used as defined in ISO 3166-1 alpha-213, ISO 639-114, and ISO 421715.

E4

Preference of standard media type name application/json

application/

own.format+json

application/json

Instead of using custom media types that contain JSON, the standard media type name application/json must be used.

E5

Definition of formats for number and integer types

properties:

  id:

   type: integer

   _____

properties:

  latitude:

   type: number

   format: float

When defining a property with the number or integer type, the precision must be indicated with format. For integer int32 and int64 are accepted; for number float and double are accepted16.

E6

Avoidance of null values for Boolean properties

properties:

 is_disabled:

  type: boolean

  description: 

   Indicates if

   a user is

   disabled.

Above API description should not return a result like this:

{

 “user”: {

  “is_disabled”:  

     null

  }

}

properties:

 is_disabled:

  type: boolean

  description: 

   Indicates if

   a user is

   disabled.

Boolean values should always be either true or false:

{

 “user”: {

 

  “is_disabled”:  

     true

  }

}

Boolean fields must only have one of two possible values: true or false. If the field can have other values, it is recommended to use a different data type.

E7

Avoidance of null values for empty arrays

properties:

 addresses:

  type: array

  description: 

   All known

   addresses of

   a user.

Above API description should not return a result like this:

{

 “user”: {

  “addresses”:   

     null

  }

}

properties:

 addresses:

  type: array

  description: 

   All known

   addresses of

   a user.

Empty arrays should be marked with [ ]:

{

 “user”: {

 

  “addresses”:  

     []

  }

}

Empty arrays must be represented as an empty list: [ ].

E8

Avoidance of plain text responses17

responses:

 ‘200’:

   content:

    text/plain:

     schema:

      type:

        string

responses:

 200’:

  content:    

   application/

   json:

     schema:

      type:

        object

All values that are returned by the API must have the Content-Type header set which specifies the format of the return value. Especially JSON objects shall not be returned as a plain text but as described in

 

2.7      Dimension F: pagination, filtering and sorting

Description
API endpoints sometimes return vast amounts of data which receivers might not be able to handle correctly. Pagination helps to create an organized stream of data and provides users with a mechanism to limit the receiving data. Filtering on the other hand is an option to query an endpoint only for relevant data. An advantage of pagination and filtering is that both help to avoid network overload and cut response times drastically. Sorting on the other hand provides a way to order data by specific criteria before it is returned via the endpoint. All three techniques help users tailor the returns of an endpoint to their specific needs and should therefore be taken into consideration. Pagination, filtering and sorting can be implemented by transforming the query parameters from the client into suitable calls to the underlying database and returning the results via the API.
Classification criteria
 

Criterion ID

Description

Example of bad practice

Example of good practice

F1

Support partial responses via filtering

[Not enabling usage of the fields parameter.]

GET /users/123?fields= (name,address)

Clients must be enabled to define the subset of fields they want to
receive via the fields query parameter.

F2

Support of pagination

[Not using offset/limit or cursor-based pagination.]

In the following example, offset/limit pagination is supported by the API. The query below would return the next 30 results, starting from the 100th entry of a list of all users:

GET /users?limit=30&offset=100

Cursor-based pagination works by setting a pointer to a specific entry in the result list and returning the next chunk of results from this point on. Pagination links are sent in the HTTP Link entity header. A header with links to the next and last pointers could look like this:

Link:

<https://api.eu/users?page=2>; rel=”next”,

<https://api.eu/users?page=8>; rel=”last”

Pagination must be supported by including offset/limit-based
pagination or cursor-based pagination in the API.

F3

Utilisation of pagination links where applicable

[Not using pagination links.]

A page with cursor-based pagination returned by the API should follow this scheme:

{

  "cursors": {

    "self": "...",

    "first": "...",

    "prev": "...",

    "next": "...",

    "last": "..."

  },

  "users": [...]

}

The API must support hypertext controls for pagination such as next, prev, first, last and self.

F4

Sorting18

[Not enabling usage of sort or sort_by parameter.]

Handling the URL parameters can be done in different ways, this is just one option:

GET

/users?sort_by=last_name&order_by=asc

The API must enable the use of sorting mechanisms. This can be done by including a URL parameter like sort or sort_by that can take a field name as its value.

 

2.8      Dimension G: sub-resources

Description
Since resources in complex systems are often related to other resources in that same system, the API design can give details about the underlying data model and thus provide hints to the user on how endpoints can be used. When nesting resources, care must be taken, as structures may become too complex if too many resources are nested under each other.
Classification criteria
 

Criterion ID

Description

Example of bad practice

Example of good practice

G1

Identification of resources and sub-resources via path segments

[Not identifying resources and sub-resources via path segments.]

/top-resources/{top-resource-id}/sub-resources/{sub-resource-id}

Resources that are not top-level resources but that are depending on another resource must be referenced by their name and identifier in the path segments.

G2

Limitation of the number of sub-resource levels

/top-resources/{top-resource-id}/sub-resource_one/sub-resource_two/ sub-resource_three/ sub-resource_four/
{sub-resource-id}

/top-resources/{top-resource-id}/sub-resources/{sub-resource-id}

The number of sub-resource (nesting) levels must be limited to three. Not following this principle could lead to an increase in URL length, violating criterion G10.

G3

Permission of optional embedding of sub-resources

[Not enabling usage of the embed parameter.]

It should, for example, be possible to display an order along with all its items:

GET /order/789?embed=(items)

The embedding of related resources can help to reduce the number of requests and must therefore be allowed in the API. This operation is comparable to a database JOIN.

 

3       Schema-compliant API Description

The API description below applies the recommendations defined in this schema. Accordingly, this API description also does not trigger any warnings by the API Friendliness Checker. It includes a standard error response as it should be contained in all API descriptions.

Please note that this description includes TLS encryption which is only available from OpenAPI v3.1.0. The current OpenAPI version 3.0.3 does not support TLS encryption yet. To comply to the current version, the security object and its description under components/securitySchemes in the sample description below must be omitted.

Example - Schema-compliant API Description
 

openapi: '3.1.0'

 

info:

  title: A sample API.

  description:

    This is an example.

  version: "2.2.0"

  x-api-id: 12345

  x-audience: company-internal

  contact:

    name: API team

    email: team@api.eu

    url: http://api.eu

 

security:

  - api_security: []

 

paths:

  '/examples':

    post:

      summary:

        API Examples

      description: Description

      requestBody:

        content:

          application/json:

            schema:

                type: string

                format: binary

      responses:

        200:

          description: OK

          content:

            application/json:

              schema:

                type: object

                properties:

                  examples:

                    type: string

        400:

          description: No such ID

          content:

            application/json:

              schema:

                type: string

                format: binary

        default:

          $ref: '#/components/responses/GeneralError'

 

components:

  parameters:

    id:

      name: id

      in: path

      description: Identifier of an example.

      required: true

      schema:

        type: string

        format: uuid

  responses:

    GeneralError:

      description: Error object

      content:

        application/problem+json:

          schema:

            $ref: '#/components/schemas/Problem'

  schemas:

      Problem:

            type: object

            properties:

              type:

                type: string

                format: uri

                description: |

                  An absolute URI that identifies the problem type.  When dereferenced,

                  it SHOULD provide human-readable documentation for the problem type

                  (e.g., using HTML).

              title:

                type: string

                description: |

                  A short, summary of the problem type. Written in english and readable

                  for engineers (usually not suited for non technical stakeholders and

                  not localized); example: Service Unavailable

              status:

                type: integer

                format: int32

                description: |

                  The HTTP status code generated by the origin server for this occurrence

                  of the problem.

                minimum: 100

                maximum: 600

                exclusiveMaximum: true

                example: 503

              detail:

                type: string

                description: |

                  A human readable explanation specific to this occurrence of the

                  problem.

                example: Connection to database timed out

              instance:

                type: string

                format: uri

                description: |

                  An absolute URI that identifies the specific occurrence of the problem.

                  It may or may not yield further information if dereferenced.

  securitySchemes:

    api_security:

      type: mutualTLS

      description: TLS encryption for API security

      name: apisecurity

 

4       Conclusion

The described assessment criteria for the SCDS classification scheme for developer-friendly APIs build on and extend the OpenAPI specification. They establish core design characteristics that developer friendly APIs should share. From the perspective of the Support Centre for Data Sharing, these should be considered as mandatory because adherence will contribute to a more user-friendly implementation, better understandability and, eventually, higher usage of compliant APIs.

 

 

  • 1. https://www.openapis.org/
  • 2. https://api-friendliness.eudatasharing.eu/
  • 3. http://spec.openapis.org/oas/v3.0.3
  • 4. https://tools.ietf.org/html/rfc7230#page-22
  • 5. Note that this criterion will be checked by v2.0 the SCDS API Friendliness Checker. This criterion also goes beyond the OpenAPI specification.
  • 6. https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
  • 7. https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
  • 8. Note that this criterion will be checked by v2.0 the SCDS API Friendliness Checker. This criterion also goes beyond the OpenAPI specification.
  • 9. https://semver.org/
  • 10. https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#data-types
  • 11. https://tools.ietf.org/html/rfc3339#section-5.6
  • 12. https://tools.ietf.org/html/rfc7231#section-7.1.1.1
  • 13. https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2
  • 14. https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes
  • 15. https://en.wikipedia.org/wiki/ISO_4217
  • 16. https://swagger.io/specification/#data-types
  • 17. Note that this criterion will be checked by v2.0 the SCDS API Friendliness Checker. This criterion also goes beyond the OpenAPI specification.
  • 18. Note that this criterion will be checked by v2.0 the SCDS API Friendliness Checker. This criterion also goes beyond the OpenAPI specification.

Support Centre for Data Sharing
Image credit:
Support Centre for Data Sharing