Technical Policy – API Standard
17/05/22 | All Guidelines and Documentation | C. Choosing the right technologyVersion 1.0
The following web-based application programming interface (API) standards guidance will help teams deliver the best possible services to users.
API STANDARD
1. USE RESTFUL
Follow the industry standard and where appropriate build APIs that are RESTful, which use HTTP verb requests to manipulate data.
When handling requests, you should use HTTP verbs for their specified purpose.
The following table illustrates the REST API Verbs:
REST Verb | Action |
---|---|
GET | Fetches a record or set of resources from the server |
OPTIONS | Fetches all available REST operations |
POST | Creates a new set of resources or a resource |
PUT | Updates or replaces the given record |
PATCH | Modifies the given record |
DELETE | Deletes the given resource |
In some cases, it may not be applicable to build a REST API, for example, when you are building an API to stream data.
2. USE HTTPS
You should use HTTPS when creating APIs.
Adding HTTPS will secure connections to your API, preserve user privacy, ensure data integrity, and authenticate the server providing the API.
Secure APIs using Transport Layer Security (TLS) v1.2. Do not use Secure Sockets Layer (SSL) or TLS v1.0. Make sure potential API users can establish trust in your certificates. Make sure you have a robust process for timely certificate renewal and revocation.
3. USE JSON
The first choice for all web APIs should be JSON where possible:
- create responses as a JSON object and not an array (JSON objects can contain JSON arrays) – arrays can limit the ability to include metadata about results and limit the API’s ability to add additional top-level keys in the future
- document your JSON object to ensure it is well described, and so that it is not treated as a sequential array
- use consistent grammar case for object keys – choose under_score or CamelCase and be consistent
Only use another representation to build something in exceptional cases, like when you:
- need to connect to a legacy system, for example, one that only uses XML
- will receive clear advantages from complying with a broadly adopted standard (for example, SAML)
4. USE UNICODE FOR ENCODING
The Unicode Transformation Format (UTF-8) standard is mandatory for use in government when encoding text or other textual representations of data.
5. LOG REQUESTS FOR PERSONAL DATA
If the API serves personal or sensitive data, you must log when the data is provided and to whom. This will help the team meet the requirements under the General Data Protection Regulation (GDPR), respond to data subject access requests, and detect fraud or misuse.
6. AUTHENTICATION/AUTHORIZATION
Authentication is required when you want to identify clients for the purposes of:
- authorization
- auditing
Store all of your credentials (i.e., username & password, application IDs, database IDs, API Keys) in some type of Secrets Manager.
DO NOT under ANY CIRCUMSTANCES store your credentials in plain text, accessible via the code, deployment scripts, or version control (Git).
The API’s purpose will dictate the security requirements for your authentication solution.
APPLICATION-LEVEL AUTHORIZATION
Use application-level authorisation if you want to control which applications can access your API, but not which specific end users. The application-level authorisation is probably not suitable for APIs holding personal or sensitive data unless you trust your consumers, for example, another government department.
We recommend using OAuth 2.0, the open authorisation framework (specifically with the Client Credentials grant type). This service gives each registered application an OAuth2 Bearer Token, which can be used to make API requests on the application’s behalf.
USER-LEVEL AUTHORIZATION
Use user-level authorisation if you want to control which end users can access your API. This is suitable for dealing with personal or sensitive data.
For example, OAuth 2.0 is a popular authorisation method in government, specifically with the Authorisation Code grant type. Use OAuth 2.0 Scopes for more granular access control.
OpenID Connect (OIDC), which builds on top of OAuth2, with its use of JSON Web Token (JWT), might be suitable in some cases, for example a federated system.
If the client is supplying SSO Tokens, you will need to determine the LOA (Level of Assurance) of their token, and whether their token has the proper LOA to access the data they are requesting.
TOKENS AND PERMISSIONS
You should:
- choose a suitable refresh frequency and expiry period for your user access tokens – failure to refresh access tokens regularly can lead to vulnerabilities
- allow your users to revoke authority
- invalidate an access token yourselves and force a reissue if there is a reason to suspect a token has been compromised.
- use time-based one-time passwords (OTP) for extra security on APIs with application-level authorisation
- use multi-factor authentication (MFA) and identity verification (IV) for extra security on APIs with user-level authorisation
- ensure the tokens you provide have the narrowest permissions possible (narrowing the permissions means there’s a much lower risk to your API if the tokens are lost by users or compromised)
7. PROVIDE USERS WITH A TEST SERVICE
Your API consumers will want to test their application against your API before they go live. Provide them with a test service (sometimes referred to as a sandbox).
8. TEST API’s COMPLIANCE AND SECURITY
You should provide your development team with the ability to test your API using sample test data, if applicable. Testing your API should not involve using production systems and production data.
Validate everything that you receive and/or send (ex: Request Body, Content, Request Headers, Input Parameters, Character length). These are the values that malicious actors most frequently try to manipulate, to see if they can elicit a different type of response from an API.
Convert and map externally displayed identifiers to your internal identifiers. NEVER display the internal identifiers (ex: database ID, customer ID, etc.) to clients. Convert them to external identifiers (preferably in GUID/UUID formats) first, and display those. Otherwise, you run the risk of exposing your internal API data to the outside world.
Utilize any of the static code scanning tools to preemptively catch and refactor any vulnerable methods in your code before deploying to production.
Use dependency scans to scan and make sure that you are not using any compromised tools, libraries, or frameworks in your code that can end up being used as backdoors or attack vectors.
9. TEST API’s PERFORMANCE AND SCALABILITY
Performance is how fast an API can deal with a single request and create a response. Scalability is the number of concurrent requests an API can deal with before it slows down significantly.
As with user-facing services, you should test the capacity of your APIs in a representative environment to help make sure you can meet demand. There are many tools available for each development environment that can help profile and monitor the API so that any performance and scalability issues can be detected during the development phase. In addition, load test an API to ensure It will perform and scale when it goes into production.
10. DOCUMENT THE API
To document an API:
- use the OpenAPI 3 Specification where appropriate for generating documentation (recommended by the Open Standards Board)
- provide sample code to illustrate how to call the API and to let users know what responses they can expect
The API documentation should include:
- contextual/overview information – what the API does, who it might be used by and under what circumstances
- business and data rules – under what circumstances is data available / not available
- error scenarios – preconditions and outcomes – including error codes and messages
- details on the test service – how to use it and how to simulate the various success and error scenarios
- full details of request and response parameters, including meaning, data type and any other constraints. Give examples of valid values
- rules on information handling, incident management and risk management
- method of authentication
- any authorisation rules, for example, use of OAuth 2.0 and specifically which scopes are required for this API
- design changes (recent and planned) and versioning information
- availability, latency, ownership, deprecation policies and status capability
- approach to backwards compatibility
- security information
REST API BEST PRACTICES
In addition to the API standards, while designing REST APIs, you need to focus on all these best practices to optimize the API design and readability to the team and other stakeholders. As a REST API developer, you should focus on security as well as the functionality of the API.
1. PREFER NOUNS OVER VERBS IN URI
Using nouns in your API URIs is the most conventional way to build descriptive API URIs.
In a RESTful API, the request verb (GET/ DELETE/ POST) tells us what the ‘operation’ is whereas the last noun in the URI tells us what type of object we are operating on.
Here are some examples which illustrate the operation and the object which is being operated on:
- GET /users (our operation is to get/ list the user objects)
- POST /users (our POST operation creates a new user)
- GET /users/1 (gets an individual user; user #1)
- PUT /users/1 (updates user #1)
- GET /users/1/orders (gets all orders for user #1)
- POST /users/1/orders (creates a new order for user #1)
2. PREFER USING PLURAL NAMING CONVENTIONS
Another RESTful convention is using plural nouns for collections. The operations we are performing make more sense when the object names are pluralised.
You wouldn’t `GET` a list of `/user`
You would `GET` a list of `/users`
And because we want to build out our API like a tree we maintain the plural naming throughout.
`/users`
`/users/1`
`/users/1/orders`
`/users/1/orders/1`
`/users/1/orders/1/items`
`/users/1/orders/1/items/1`
3. UTILIZE RESOURCE NESTING EFFICIENTLY
Resource nesting should be carefully considered from the position of your API users. Sub-resources can be a powerful feature to allow your users to make fewer calls to your API but they often come at the price of greater server-side processing on the part of the API and increased complexity for the API URIs
For example:
`/users/1/orders/1/items` could be a useful endpoint. It could be used to show a user a summary of their order.
`/users/1/orders/1/items/1` could be used to show a summary of an item. But this doesn’t particularly benefit from being nested as your API will have other ways to look up items. In this case getting a list of items with `/users/1/orders/1` then making a request to `/items/1` for the item detail is probably the right choice.
4. DATA FILTERING OPTIONS
When dealing with large datasets it is a good practice to allow API users to retrieve only relevant data. This can be achieved by using a filter that will match data that satisfies the required criteria. Minimizing the data returned can save bandwidth between the API and the client and memory utilization by the API itself.
Common REST API filtering options include:
Filtering
Sorting
Paging
Field Selection
FILTERING
Using this you can filter results that satisfy your required conditions. You can use search parameters like country, creation, date, etc for this.
GET /users?country=CY
GET /users?creation_date=2021-10-11
GET /users?creation_date=2021-10-11
You should only allow query strings to be used in GET requests for filtering the values returned from an individual resource, for example, /users?state=active or /users?page=2.
You should never use query strings in GET requests for identification purposes, for example, avoid using the query string /users?id=1.
Query strings should not be used for defining the behaviour of your API, for example /users?action=getUser&id=1
SORTING
You can provide your users with the option to specify the sort order of their results in ascending and descending order using this option.
GET /users?sort=birthdate_date:asc
GET /users?sort=birthdate_date:desc
OFFSET-BASED PAGINATION
Using the ‘limit’ option, you can narrow down the results to the required number. You can also use ‘offset’ to show the part of the overall results displayed.
GET /users?limit=120
GET /users?offset=3
Query Parameter | Type | Default | |
---|---|---|---|
offset | Integer | 0 | The (zero-based) offset of the first item in the collection to return |
limit | Integer | Depends on API | The maximum number of entries to return. If the value exceeds the maximum, then the maximum value will be used. |
FIELD SELECTION
Using the field selection function, you can request a specific subset of the data fields available for that object rather than receiving all the fields (which should be the default behaviour).
For example, while our `user` object may have any number of fields such as ‘email’, ‘telephone number’, ‘address’, ‘birth date’ or ‘creation date’ you might want to retrieve just the birth date and email to automate birthday wishes. You could use a query like this:
For a specific user:
GET /users/123?fields=name, birthdate, email
For a full list of users:
GET /users?fields=name, birthdate, email
5. USE OF ENUMS
Enums, or enumerated types, are variable types that have a limited set of possible values. Enums are popular in API design, as they are often seen as a simple way to communicate a limited set of possible values for a given property. It is a good practice to use enums when necessary to provide a complete set of possible returned or given values for a given property thus making data handling an easier task.
6. HTTP STATUS CODES
There are many ways in which REST developers can tackle error handling. As a best practice, HTTP status codes must be based on the request result. For example, if a request has no content the appropriate “204 – No Content” HTTP status is sent. Or if a new record is being created the appropriate “201 – Created” HTTP status is sent.
So, standardized error codes along with the provision of proper documentation are also recommended good practices in API development.
For example,
Successful responses from the API must return standardized status codes.
HTTP status | Description |
---|---|
200 Success | Standard response for successful HTTP requests. The actual response will depend on the request method used. |
Use appropriate status codes for different categories of errors.
HTTP status | Description |
---|---|
400 Bad Request | The server rejected the request as invalid. |
401 Unauthorized | The authentication associated with the request is invalid |
403 Forbidden | Authentication is valid, but user is not allowed to perform this operation |
404 Not Found | A required object for this operation was not found |
500 Internal ServerError | The server encountered an unexpected error while processing the request |