REST guidelines

Prev Next

In Nexus Link, we rely on REST for technology agnostic web service communication.

There is a high degree of freedom on how to implement REST. It is important to limit that freedom within one digital platform to make the code consistent and easier to maintain. We suggest the following "limitations" for the business API and the Nexus adapters.

HTTP status codes

For Nexus Link, we have decided to just use a few status codes from the long list of HTTP status codes. This decision was inspired by a couple of blog posts about the importance of reducing the number of status codes. "Preferably 3, no more than 8", most prominently REST API Error Codes 101.

We have limited the status codes for API responses to the following selection:

  • 200 OK
  • 202 Accepted
  • 204 No Content
  • 400 Bad Request
  • 500 Server Error

Please note that these are the status codes that the API code will return. There are other parts, such as the application server or the load balancer, that can return other status codes, so a REST client will need to handle those too. Some common example:

  • 401 Unauthorized, e.g. if there is something wrong with your token
  • 404 Not Found, e.g. if you have misspelled the name of a resource
  • 502 Bad Gateway, e.g. if the load balancer thinks there is something wrong with one of the application servers
  • 503 Service unavailable, e.g. if the service is starting up and not yet available

200 OK

The request was successful and the requested entity is returned.

Example 1 (response for a GET for the football team with Id 72):

200 OK
{
    "Id": "72",
    "Name": "Hammarby IF",
    "City": "Stockholm",
    "League": "Allsvenskan",
    "HomeArenaId": "49"
}

Example 2 (response for a POST for a new football team):

200 OK
"73"

202 Accepted

The request has been accepted, but it has not yet completed. Information about the ongoing process is returned.

Example (response for a POST to make an asynchronous operation).

202 Accepted
{
    "Location": "https://example.com/Results/832743",
    "RecommendedTimeToWaitInSeconds": 2.0
}

The information must as a minimum contain the following fields:
; Location
: A URL that points out where information about the request can be found (using GET).
; RecommendedTimeToWaitInSeconds
: The number of seconds you should wait before you check the result at the given location.

204 No Content

The request was successful, but there was nothing to return.

Example:

204 No content

400 Bad Request

The request failed, due to some error on the calling party side (the "client"). More information about the problem is returned. The format for this information is strongly regulated, see Error response body below.

Example (the request was a PUT for a person, but the mandatory field "GivenName" was empty):

400 Bad Request
{
    "TechnicalMessage": "The field \"GivenName\" must not be empty.",
    "FriendlyMessage": "The request contained data that was syntactically wrong, had values out of range, or something similar.",
    "MoreInfoUrl": "http://lever.xlent-fulcrum.info/FulcrumExceptions#ServerContractException",
    "IsRetryMeaningful": false,
    "RecommendedWaitTimeInSeconds": 0.0,
    "ServerTechnicalName": "bt-api",
    "InstanceId": "0be62921-9263-4195-bb6f-6a5b865c7f0a",
    "Code": "342532",
    "Type": "Xlent.Fulcrum.ServiceContract",
    "CorrelationId": "38fa8dba-137b-446d-8762-2cd47703113b"
}

500 Internal Server Error

There was an error on the called side (the "server"). More information about the problem is returned. The format for this information is strongly regulated, see Error response body below.

Example (A GET request failed because the database could not be reached):

500 Internal Server Error
{
    "TechnicalMessage": "The database could not be reached.",
    "FriendlyMessage": "The resource was temporarily locked, please try again.",
    "MoreInfoUrl": "http://lever.xlent-fulcrum.info/FulcrumExceptions#TryAgainException",
    "IsRetryMeaningful": true,
    "RecommendedWaitTimeInSeconds": 10.0,
    "ServerTechnicalName": "customer-master",
    "InstanceId": "15ddee43-1f60-46ee-b7a4-796d876b24a2",
    "Code": "4873",
    "Type": "Xlent.Fulcrum.TryAgain",
    "CorrelationId": "d505f7e2-9169-40d6-94d0-38c7baddb9a6"
}

Error response body

When we return an HTTP status code of 400 or 500, we want to have a consistent response body. Here are the posts that where the initial inspiration when we defined our response body:

This is the format for the Nexus Link error response body.

Mandatory fields

Field Description
TechnicalMessage Technical information that a developer might find useful. This is where you might include exception messages or anything else that you think will help a developer.
Type Errors are grouped into different types, such as "BusinessRule", "NotFound", "Unavailable". Type is a unique id for the type of error. The recommendation is to use human readable strings. See error types for the types that we use.
IsRetryMeaningful Indication to the caller if it could be meaningful to try sending the request again without changing it. Examples: If a resource is temporary locked, then this should be set to true. If the request did not fulfill the contract, then this should be set to false.
InstanceId A unique identifier for this particular instance of the error. Ideally, the same identifier should not be used ever again. The recommendation is to use a newly created GUID.

Optional fields

Field Description
CorrelationId All calls that were involved in the chain that led to this error (successful calls or not) should all be marked in the logs with the same correlation id. This comes in very handy if someone wants to track down exactly what happened.
FriendlyMessage A human readable error message that can potentially be shown directly to an application end user (not a developer). It should be friendly, easy to understand and give some directions on how to proceed as a user. Avoid technical information.
MoreInfoUrl An URL that anyone seeing the error message can click (or copy and paste) in a browser. The target web page should describe the error condition fully, as well as potential solutions to help them resolve the error condition.
RecommendedWaitTimeInSeconds If ''IsRetryMeaningful'' is true, then this property can give a hint on how long to wait before the request is sent again. A value equal to 0.0 (or less) should be interpreted as no recommendation was given.
ServerTechnicalName A technical name for the server or source application that created this error information.
ErrorLocation A hint on where in the code that the problem was found. This is mainly use for status code 500 and the objective is to provide some information for locating problems
InnerError If this error message was created due to another error, then this contains the full information about that "inner" error, with the the same format as we are describing here.
InnerInstanceId If this error message was created due to another error, this will be the InstanceId of that error, even if there is not a full InnerError. This way you can find the correspoinding log.
Code This fields give the developer a possibility to have more detailed error codes within an error type. Typically to provide automatic handling of specific errors.

Example

A GET request failed because the database could not be reached:

500 Internal Server Error
{
    "TechnicalMessage": "The database could not be reached.",
    "FriendlyMessage": "A resource was temporarily locked, please try again.",
    "MoreInfoUrl": "http://lever.xlent-fulcrum.info/FulcrumExceptions#TryAgainException",
    "IsRetryMeaningful": true,
    "RecommendedWaitTimeInSeconds": 10.0,
    "ServerTechnicalName": "customer-master",
    "InstanceId": "15ddee43-1f60-46ee-b7a4-796d876b24a2",
    "Code": "4873",
    "TypeId": "Xlent.Fulcrum.TryAgain",
    "CorrelationId": "d505f7e2-9169-40d6-94d0-38c7baddb9a6"
}

Status code to error type

When a REST service doesn't return one of the known status codes or error types, then we recommend to interprete the response according to this table:

Status code FulcrumError
300 catch all Xlent.Fulcrum.ServiceContract
400 Bad Request Xlent.Fulcrum.ServiceContract
401 Unauthorized Xlent.Fulcrum.Unauthorized
402 Payment Required Xlent.Fulcrum.ServiceContract
403 Forbidden Xlent.Fulcrum.ForbiddenAccess
404 Not Found Xlent.Fulcrum.ServiceContract
405 Method Not Allowed Xlent.Fulcrum.ServiceContract
406 Not Acceptable Xlent.Fulcrum.ServiceContract
407 Proxy Authentication Failed Xlent.Fulcrum.Unauthorized
408 Request TimeOut Xlent.Fulcrum.TryAgain
409 Conflict Xlent.Fulcrum.Conflict
410 Gone Xlent.Fulcrum.NotFound
4xx catch all Xlent.Fulcrum.ServiceContract
500 Internal Server Error Xlent.Fulcrum.AssertionFailed
501 Not Implemented Xlent.Fulcrum.NotImplemented
502 Bad gateway Xlent.Fulcrum.Resource
503 Service Unavailable Xlent.Fulcrum.TryAgain
504 Gateway Timeout Xlent.Fulcrum.TryAgain
505 HTTP Version Not Supported Xlent.Fulcrum.NotImplemented
5** catch all Xlent.Fulcrum.AssertionFailed