Best Practices for RESTful API Design

Best Practices for RESTful API Design

In today’s highly connected environment, RESTful APIs are dominating the fabric of the internet. Most of the applications being developed today have REST APIs as the backbone. Virtually all cloud based services rely on REST APIs including Amazon, Twitter, Facebook, Google, YouTube and many more. Companies far and wide are using REST APIs in their B2B and B2C transactions including BestBuy, Target, Macy’s, Walmart, and Walgreens.

What is REST?

REST (REpresentational State Transfer) is an architectural style, and an approach to communications that is usually used when developing Web services. REST has gained in popularity over its contender SOAP (Simple Object Access Protocol) because REST is lighter in terms of bandwidth usage. RESTful services are much easier to implement and scale than SOAP. Thus REST is the chosen architecture by service providers like Facebook, Twitter, Amazon, Microsoft, and Google.

REST architecture describes six constraints. These constraints were described in Roy Fielding’s dissertation as Uniform Interface, Stateless, Cacheable, Client-Server, Layered-System, and Code On Demand.

  • Uniform Interface – Resources are manipulated via CRUD (create, read, update, delete) operations. CRUD operations are managed via PUT, GET, POST, and DELETE request methods.
  • Stateless – In REST the state is contained within the request itself, or as part of the URI, query-string parameters, body or in the headers. After processing the request, the state may be communicated back via the headers, status or response body.
  • Cacheable – Responses from the web service to its clients are explicitly labeled as cacheable or non-cacheable. This way, the service, the consumer, or one of the intermediary middleware components can cache the response for reuse in later requests.
  • Client Server – This is a key constraint, as it based on separations of concerns. The client/server requirement ensures that a distributed environment exists. It requires the client that sends requests and a server component that receives the requests. After processing the request, the server may return a response to the client. Error responses may be transmitted as well, which requires the client to be responsible for taking any corrective action.
  • Layered System – A client should not be able to tell whether it is connected directly to the end server, or to an intermediary along the way. Intermediary servers may add security policies, or improve scalability.
  • Code On Demand – This is an optional constraint. It allows a client to have logic locally via the ability to download and execute code from a remote server.

So now that we know the “What” let’s delve into the “Why”.

Why Use REST APIs?

  • REST uses all of the standard HTTP protocol methods — namely GET, POST, PUT, DELETE and PATCH together with URLs more specifically URIs. It is based in standard HTTP operations using individual URIs for added flexibility.
  • REST separates operations between client and the server. It vastly improves portability by allowing the developers to use any front-end technology they wish including AngularJS, Bootstrap, VUE, Ember, ReactJS, CycleJS, ExtJS, PHP, .NET, or even HTML5 with jQUERY or other Javascript frameworks. It has even proven itself by allowing legacy systems to use RESTful APIs easily for real-time interactions. There are several large consulting companies that perform these upgrades among them but not limited to is ThoughtWorks, which has been involved in several large scale legacy replacement projects.
  • REST is simpler than SOAP. This is because of the added overhead that comes with SOAP. The way that this is depicted in many internet forums is to think of SOAP as an envelope with extra overhead required for opening and sealing the envelope, extra bytes required and extra bandwidth. The envelope contains the namespace, encoding style, header, body, and error handling. REST, on the other hand, is akin to a postcard which is lightweight, requires less bandwidth and does not require opening/closing and is much easier to work with.
  • REST is optimized for the web, since it relies on HTTP protocol. Additionally, since it’s main data format is JSON, it is basically compatible with all internet browsers.
  • REST permits many different data formats. It supports plain text, HTML, XML, JSON, ZIP, PDF, PNG, JPG, MSWORD, XLS, PPT, CSV, etc. You get the idea, all media types/formats supported by the HTTP protocol.
  • REST is way more popular than SOAP

    I’ll let this Google Trends chart speak for itself. Here we compare SOAP API vs REST API and show the popularity that REST has over SOAP.

    soap_vs_rest

  • REST features excellent performance and scalability. REST performance comes from the fact that there is little overhead over HTTP protocol. SOAP, being a protocol contains much more complexity and overhead in terms of increased security and specifications (WSDL, XSDs, SOAP, WS-Addressing and WS-Security). With REST if you need to scale, you can easily migrate the backend to larger, higher performance servers, and/or cluster the environment with ease.
  • REST is rapidly evolving — enter GraphQL. This is considered by many as REST 2.0 and the new and much improved REST developed by the folks at Facebook. GraphQL has been implemented in multiple languages, including JavaScript, Python, Ruby, Java, C#, Scala, Go, Elixir, Erlang, PHP, and Clojure.

In this tutorial we will discuss how to design RESTful Web Services following some of the industry’s Best Practices for building RESTful APIs.

Best Practices

Use Nouns / not Verbs for the endpoints

When building your RESTful API make sure you use nouns as your resources instead of verbs. Instead of creating URLs with endpoints containing (verb-noun combinations: hyphenated, snake_case, camelCase):

/createusers
/deleteassociates
/deleteAssociates
/delete-associates
/delete_associates
/updateVendors
/update-vendors

You should opt for RESTful endpoints that look like:

/users
/associates
/vendors

Please Note:

You will note that I am using plural nouns for the RESTful endpoints. Additionally, you will use the HTTP methods as verbs: GET, POST, PUT, DELETE and PATCH.

Using these HTTP Action Verbs will allow you to perform all CRUD operations. When you want to create a new record you should be using POST. If you are trying to read a record, you should be using GET. To update a record you should be utilizing PUT or PATCH. And to delete a record, you should be using a DELETE.

#URIMethodDescription
1/usersGETRetrieves all of the users
2/users/123GETRetrieves user with ID equal to “123”
3/usersPOSTCreates a new user
4/users/123PUTUPDATES a user with ID equal to “123”
5/users/123DELETEDeletes a user with ID equal to “123”
6/users/123/carsGETRetrieves all of the cars associated with user with ID equal to “123”
7/users/123/cars/2777GETRetrieves car with ID equal to “2777” associated with user with ID equal to “123”

Use Plural Nouns

Stick with the standard rule — Use plural nouns for all REST endpoints. Although, you may think it is fine to mix resources from single instances to plural the simple rule is to all use plural nouns for all resources.

/users instead of /user
/associates instead of /associate
/vendors instead of /vendor
/groups instead of /group

Describe resource functionality with HTTP methods

GET Methods should not alter state

The GET method should only be used to retrieve records. If you need to alter the state you should use POST, PUT, DELETE and lesser used PATCH methods.

Provide ample feedback to help developers succeed

Some APIs take a minimalist view, returning only the HTTP status codes (201-CREATED or 204-NO CONTENT) and although this is not incorrect, I prefer to provide more detailed status messages as JSON/XML responses to give the APIs users as much information as possible in order to succeed. Whether they decide to use it or not will be entirely up to them.

Minimalist Approach

DELETE /restservices/v2/users/tu0001

HTTP/1.1 204 NO_CONTENT
X-Application-Context: application:0
Content-Type: application/json;charset=UTF-8
Content-Length: 57

More Detailed Approach

DELETE /restservices/v2/users/tu0001

HTTP/1.1 200 OK
X-Application-Context: application:0
Content-Type: application/json;charset=UTF-8
Content-Length: 57

{"status_code":200,"message":"User deleted Successfully"}

Updates & Creates return a resource representation

The POST, PUT, or PATCH methods may modify one or more fields in the underlying resources. As previously stated, returning all of the details during the updates and creates will avoid having the developer make another API call to get the resource representation after the API call.

Show relationship using sub-resources

If you want to show relations in URIs you can do that using sub-resources but you need to ensure that this is done correctly and it makes sense to the users of the REST APIs.

For example, let’s say we wish to show relationships with customers and orders:

Without using this technique we might be forced to implement one of several other URIs:

Approach #1

Using a GET /orders/?customerid={id} we would return all of the orders belonging to customer with id={id} where {id} is some value (e.g 123).

We can use the “belongs-to” relationship with a URI as follows:

Approach #2

Using a GET /customers/{id}/orders we would return all of the orders belonging to customer with id={id} where {id} is some value (e.g 123).

Please Note:

Now you may realize that using the second approach may make more sense. The only time approach number #1 makes more sense is when multiple parameters will be needed.

Version your APIs

You should always try to version your APIs. By versioning your APIs you will allow for an upgrade path without introducing some breaking change into existing APIs. Additionally, you can notify consumers of the API that new versions are available at the following fully-qualified URIs ….

REST_URL_structure

https://localhost:8443/app-context/v1/users/{id}

Introduction in any major breaking update can be avoided with the following /v2.

https://localhost:8443/app-context/v2/users/{id}
https://localhost:8443/app-context/v2/vendors/{id}/orders

RESTful Error Handling and Status Response Messages

Your API should provide meaningful error messages and not simply return 400 Bad Request error response code. Your API should return useful error messages in a common prescribed format.

A typical error message format should, at a minimum, return an error code and a message field.

{
 "status_code" : XXX,
 "message" : "Oops, something bad happened here"
}

Or even more details:

{
 "status_code" : XXX,
 "message"  : "Oops, something bad happened here",
 "details" : "Provide some add'l details about the cause..."
}

And for multi-field validations you may want to return an errors array containing the fields that failed the validations. This way all the validations show up in one response message to your API consumers instead of resulting in multiple round trip exchanges with your API clients. Something like this often works best:

{
 "status_code" : XXX,
 "message"  : "Validations failed",
 "details" : "The following validations failed...",
 "errors": [
    {
      "status_code" : XXX,
      message: "'cn' is a required field for this request"
    },
    {
      "status_code" : XXX,
      message: "'firstname' is a required field for this request"
    },
    {
      "status_code" : XXX,
      message: "'lastname' is a required field for this request"
    }
 ]
}

Using Standard HTTP Status Codes

The API should follow standard HTTP status codes convention. These HTTP response status codes are returned whenever website visitors or search engines make a request to web server. These three-digit numeric codes indicate whether a particular request was successful or unsuccessful.

1xx (Information Class)

This class of status code is considered experimental and should not be used. This status code does not require headers. The HTTP/1.0 protocol did not define any 1xx status codes and as such is highly encouraged that servers MUST NOT send a 1xx response.

2xx (Success Class)

This class of status codes indicates that the client request was successfully received and processed by the server.

  • 200 Ok – The request has succeeded. The information returned with the response is dependent on the method used in the request.
  • 201 Created – The request has been fulfilled and resulted in a new resource being created. This should be the standard HTTP status code using the POST method when a resource is created.
  • 202 Accepted – The request has been accepted for processing, but the processing has not been completed. The request might or might not eventually be acted upon. In this case, returning a JSON response message similar in format to the error message may be helpful especially if coupled with some sort of transaction_id. This transaction_id can later be used to poll and ensure proper request completion.
  • 204 No Content – The request has been fulfilled by the server and there is no content to send in the response body. This is typically used with the DELETE method when no content need be returned. It may also be used with PUT method when performing an UPDATE of the resource and information does not need to be updated.
  • Please Note:

    I personally recommend using a 200 OK with the actual updated resource representation instead of using the 204. I stand by my mantra as stated previously of providing ample feedback to help developers succeed. In many cases this will avoid having the developer make another API call to get the resource representation after the initial API call.

3xx (Redirection Class)

  • 301 Moved Permanently – The requested resource has been assigned a new permanent URI and any future references to this resource SHOULD use one of the returned URIs.
  • 304 Not Modified – This response code indicates that there is no need to retransmit the requested resources. It is an implicit redirection to a cached resource. This happens when the browser caches data, it also stores the Last-Modified or ETag header from the server.

4xx (Client Error Class)

  • 400 Bad Request – This response status code indicates that the server could not understand the request due to some invalid syntax. The client needs to modify this request before retransmitting.
  • 401 Unauthorized – This error status response code indicates that the request has not been processed because it lacks valid authentication credentials for the target resource.
  • 403 Forbidden – This error status response code indicates that the request has not been processed because it lacks valid authentication credentials for the target resource. If the server does not wish to make this information available to the client, the status code 404 (Not Found) can be used instead.

    NOTE: I personally do not agree with this approach as it will give clients an indication that the RESTful endpoint is no longer available instead of credentials problem.

  • 404 Not Found – This error response code indicates that a server can not find the requested resource. This response code probably is the most famous one due to its frequency to occur in the web.
  • 405 Method Not Allowed – The request method is known by the server but has been disabled and cannot be used. For example, we may forbid using DELETE on a specific resource.
  • 406 Not Acceptable – This response is sent when the web server, after performing server-driven content negotiation, doesn’t find any content following the criteria given by the user agent. We can use this error status response code to indicate that one of the conditions or parameters is
  • 409 Conflict – This response is sent when a request conflicts with the current state of the server.
  • 412 Precondition Failed – The client has indicated preconditions in its headers which the server does not meet.

5xx (Server Error Class)

  • 500 Internal Server Error – The server has encountered a situation it doesn’t know how to handle.
  • 503 Service Unavailable – The server is not ready to handle the request. Common causes are a server that is down for maintenance or that is overloaded.

For a definitive list of status code definitions, please visit www.w3.org

Use SSL for added security – All the time

In today’s world we should be using SSL/TLS for all of our connections. However, it still incredibly common to see Non-SSL (HTTP) connections in many places in the corporate landscape as well as (libraries, shops, cafe’s, retailers, etc). These open communications allow for easy snooping and eavesdropping and can compromise your credentials if you inadvertently connect and use their Wi-Fi Hotspots. Personally, I use a VPN service with strong encryption protocols on my devices anytime I am using a public Wi-Fi Hotspot.

In addition to using SSL for encryption we should take the proper precautions and perform the following in our API:

  • Protect HTTP methods – RESTful API often use POST, GET, PUT and DELETE for CRUD operations, that is Creating, Reading, Updating and Deleting. We need to ensure the incoming HTTP method is valid for the token/API key and associated API user has access to the resource collection, action, and record.
  • Protect privileged actions and sensitive resource collections – Again, not every user has a right to every RESTful endpoint provided by our web service. This is vital, as you don’t want administrative web services to be misused.
  • Protect against cross-site request forgery – For resources exposed by RESTful web services, it’s important to make sure any PUT, POST, and DELETE request is protected from Cross Site Request Forgery. We recommend using a token-based approach.
  • Protect against insecure direct object references – If you had a bank account REST web service, you’d have to make sure there is adequate checking of primary and foreign keys:
  • It is absurd to allow something like this, without performing extensive validations:

    /app-context/v2/account/87228723/transfer?amount=$10000.00&toAccount=2398239
  • Perform Input validations – We should perform input validation for all of our user interface applications and this applies to RESTful web services, but even more so because automated tools can easily pound away at your interfaces for hours on end at extreme velocity.

These recommendation follow the standards put forth by OWASP.org. The bullet points provided are only a few of the important points I wanted to discuss in this forum, however, for a comprehensive list please visit OWASP directly.

Time of leave XML behind, JSON only responses

jsonvsxml

Additional thoughts

Create Resources for Metadata

By providing a resource for metadata to your clients you will allow them to inquire as to the structure and characteristics of the resources available to the API. This will allow them to programmatically extract information by using your API. This is helpful to get a list of all possible fields available and make it available for checkboxes or listbox (dropdowns). Jira, Salesforce and IBM just a few of the many vendors that implement this in their API services.

Let’s take a look at a typical resource, our USER resource.

{
  "cn" : "av0001",
  "firstname" : "amaury",
  "middleinitial" : null,
  "lastname" : "valdes",
  "fullname" : "amaury valdes",
  "employeestatus" : "a",
  "emailaddress" : "amaury@example.com",
  "groupmembership" : null
}

The typical URI might be something like:

https://localhost:8443/app-context/v2/users/metadata

A response containing the metadata for the USER resource, might be something like:

{
  "name" : "User",
  "fields" :
  [
    {
      "length" : 36,
      "name" : "cn",
      "type" : "id",
      "defaultValue" : { "value" : null  },
      "updateable" : false,
      "label" : "Common Name"
    },
    {
      "length" : 20,
      "name" : "firstname",
      "type" : "String",
      "defaultValue" : { "value" : null  },
      "updateable" : true,
      "label" : "First Name"    
    },
    {
      "length" : 40,
      "name" : "lastname",
      "type" : "String",
      "defaultValue" : { "value" : null  },
      "updateable" : true,
      "label" : "Last Name"   
    },
    ...
  ]
}

Use Links for Relationships (HATEOAS)

The concept of including links in REST responses has been around since it’s inception. Roy Fielding actually defined HATEOAS as a constraint and an essential part of the “uniform interface” in his doctoral dissertation. HATEOAS is an acronym for Hypermedia as the Engine of Application State. This simply states that a REST client needs no prior knowledge about how to interact with the application beyond understanding hypermedia. Once a client enters the application via the original fixed URL, all other interactions are discovered from the REST responses returned from the server via the embedded links provided.

{
  "cn" : "av0001",
  "firstname" : "amaury",
  "middleinitial" : null,
  "lastname" : "valdes",
  "fullname" : "amaury valdes",
  "employeestatus" : "a",
  "emailaddress" : "amaury@example.com",
  "groupmembership" : null,

  "links": [{
        "rel": "self",
        "href": "https://localhost:8443/app-context/v2/users/av0001"
      },
      {
        "rel": "orders",
        "href": "https://localhost:8443/app-context/v2/users/av0001/orders"
      }
  ]
}

Result Sorting, Selecting and Filtering

Other useful features to include in your APIs sorting, selecting and filtering. Instead of creating new endpoints with a limited set of values you could modify existing endpoints and allow your API clients to limit or restrict the criteria and or return the results in sorted fashion by building these features into your existing endpoints and pass the input parameters with query parameters.

Sorting

Here we use the query parameter sort and pass it the field we wish to sort on (using a minus for reverse or descending order).

Sorting Example

/app-context/v2/users/lastname/{lastname}?sort=firstname

And to reverse sort you might do the following, with the minus sign:

/app-context/v2/users/lastname/{lastname}?sort=-firstname

Ascending Order Example

[
  {
    "cn" : "av0001",
    "firstname" : "amaury",
    "middleinitial" : null,
    "lastname" : "valdes",
    "fullname" : "amaury valdes",
    "employeestatus" : "a",
    "emailaddress" : "amaury.valdes@example.com",
    "groupmembership" : null
  },
  {
    "cn" : "hr0029",
    "firstname" : "henry",
    "middleinitial" : null,
    "lastname" : "richards",
    "fullname" : "henry richards",
    "employeestatus" : "a",
    "emailaddress" : "henry.richards@example.com",
    "groupmembership" : null
  },
  {
    "cn" : "jc0007",
    "firstname" : "jeff",
    "middleinitial" : null,
    "lastname" : "cameron",
    "fullname" : "james cameron",
    "employeestatus" : "a",
    "emailaddress" : "james.cameron@example.com",
    "groupmembership" : null
  },
  {
    "cn" : "jw0023",
    "firstname" : "john",
    "middleinitial" : null,
    "lastname" : "williams",
    "fullname" : "john williams",
    "employeestatus" : "a",
    "emailaddress" : "john.williams@example.com",
    "groupmembership" : null
  }
]

Selecting

The rational for using a selection process is for performance reasons. By limiting the amount of data being returned by the server and only returning the data the API customer is interested in network, CPU and memory footprints can be reduced which will further improve system performance both on the server side and application side.

There are several other APIs that already follow this standard including Google Apis:

Request for a partial response Google allows for partial response instead of the full resource representation by using the fields parameter to significantly reduce the amount of data returned.

Google Selecting for Partial Response

https://www.googleapis.com/demo/v1?key=YOUR-API-KEY&fields=kind,items(title,characteristics/length)

Selecting Example

Here we use the query parameter fields and pass it a comma-separated list of fields we wish to perform our selection on. At this point only those fields matching the selection criteria on will be displayed.

/app-context/v2/users/lastname/{lastname}?fields=firstname,lastname,fullname
[
  {
    "firstname" : "henry",
    "lastname" : "richards",
    "fullname" : "henry richards"
  },
  {
    "firstname" : "amaury",
    "lastname" : "valdes",
    "fullname" : "amaury valdes"
  },
  {
    "firstname" : "john",
    "lastname" : "williams",
    "fullname" : "john williams"
  },
  {
    "firstname" : "jeff",
    "lastname" : "cameron",
    "fullname" : "james cameron"
  }
]

Simple Rules to Follow

  • Don’t include a trailing forward slash in your URI. It adds nothing and might cause confusion or issues for your clients.
  • Forward slashes should be used to indicate hierarchical relationships. Put them in the path portion to show the relation between resources.
  • Use hyphens to improve readability. Developers need to be able to quickly scan and interpret URIs and hyphens function like spaces to improve the readability of long paths.
  • Avoid underscores in the for your endpoints. Some apps may underline the URIs thus hiding your underscores and frustrating your end users

That’s It!

I hope you enjoyed this Post. It was certainly a lot of fun putting it together. Please continue to share the love and like us so that we can continue bringing you quality posts.

best_practices

Please Share Us on Social Media

Facebooktwittergoogle_plusredditpinterestlinkedinmail

Leave a Reply

Your email address will not be published. Required fields are marked *