Modeling REST Services Part 2 – Basics

Modeling the REST URI

Basics

In my experience, the following guidelines are helpful to model a clear understandable REST URI. Keep in mind, that there are many different opinions on what makes an easy to understand URI. I am not aware of any clear standard.

Basic URI structure

The URI would consist of the protocol, followed by the chosen primary domain name and business domain name or service name. The resource’s name would come next. If doing a single GET, UPDATE, or DELETE operation, the resource’s ID would follow. Instead of doing camelCase or PascalCase to separate words in the URI, a hyphen is used. For example, instead of /SalesOrderService/ or /salesOrderService/, use /sales-order-service/.

protocol://primary-domain.com/domain-name/resource-name/resource-id

http://my-domain.com/sales-order-management/orders/83837821

protocol://primary-domain.com/service-name/resource-name/resource-id

http://my-domain.com/sales-order-service/orders/83837821

For the examples in this article, I removed the primary domain name to give us a sort of shorthand.

protocol://service-name/resource-name/resource-id

http://sales-order-service/orders/83837821

Examples of REST URI for Each HTTP Method

While there are other HTTP methods not discussed here, the most prominently used are the following:

POST = Create
GET = Retrieve
PUT = Update
DELETE = Delete

Create Order (pass the order XML or JSON in the HTTP body)

POST https://order-service/orders/

Updating Order (pass the order XML or JSON in the HTTP body)

PUT https://order-service/orders/{order ID to update}

Retrieve Order (returns the order XML or JSON in the HTTP body)

GET https://order-service/orders/{orderID to retrieve}

Retrieve All Orders (returns the orders XML or JSON in the HTTP body)

GET https://order-service/orders

Delete Order

DELETE https://order-service/orders/{orderID to delete}

Plural versus Singular Resource

The resource, in most cases, should be expressed as plural. For example

/orders

should be preferred over

/order

Imagine using the singular for an order to retrieve both singular and plural.

Plural GET

/order 

Singular GET

 /order/83837821 

You can see it does not quite make sense. The Plural GET URI describes an order in the singular to return orders. Are you asking for order or orders? The Singular GET URI describes an order in the singular but also has a unique resource ID associated. The word order implies that there is only one resource, not many, so why would you need a resource ID to get a resource that is a singleton?

In the rare case, where there will always only be one instance of a given resource singular makes sense.

Singular GET (there is only entity of this type in the system and will always only be one)

/order 

For a list of all of the orders in a system, use the following URL.

GET http://sales-order-service/orders

The payload returned would contain a list of orders. Perhaps an orders entity is returned, and it provides a list of order entities.

JSON (camelCase)

  { "entries": [
             {"orderId": "83837821", ... },
             {"orderId": "83837811", ... },
             ...
            ]
  }

XML (camelCase)

<orders>
 <entries>
 <order orderId="83837821" ... />
 <order orderId="83837811" ... />
 </entries>
</orders>

A URI that is used to return all of the given resources for a particular resource type is not practical in most cases without having filtering rules or paging constraints. The system should not try to return all of the orders in one single call. See the Paging and Filtering section for examples.

To retrieve a particular resource, you would use the resource’s ID. In this case, it is an order ID.

GET http://sales-order-service/orders/83837821
 
{ 
    "orderId": "83837821",
    ... 
} 

Resource ID – Unique Identifier

The resource ID should be a unique identifier within the context of the resource URI’s hierarchy. The below sales-order-service is used within a single company that has a single order management system. The ID 83837821 is a unique identifier within all of the orders stored.

GET http://sales-order-service/orders/83837821 

Resource ID – Composite Key

Imagine that you had a service that is used by several companies or your single company needed multiple order management system instances. The key might be a composite key which is unique only by combining one or more keys. Here is an example of one service where the same number is applied for different orders. That is because it is unique when considering the name of the company where the order exists. The company name (actually a company code), in this case, is part of a composite that along with order number makes a unique identifier for the order.

Example 1

Gon’s Fishing Tackle Online Sales

GET http://sales-order-service/gon-fishing-tackle/orders/83837821 

Killua’s Skateboards Online Sales

GET http://sales-order-service/killua-skateboards/orders/83837821 
Example 2

Gon’s Fishing Tackle Online Sales

GET http://sales-order-service/GONFISTAK/orders/83837821 

Killua’s Skateboards Online Sales

GET http://sales-order-service/KILSKATBRD/orders/83837821 
Example 3

Gon’s Fishing Tackle Online Sales

GET http://sales-order-service/123457/orders/83837821 

Killua’s Skateboards Online Sales

GET http://sales-order-service/123456/orders/83837821 

Resource ID – Resource Key Versus Regular Property

A property of a particular order should not be used as the resource ID. For example, someone might try to model a service call to get all orders for a given customer ID 5432341 like this.

GET http://sales-order-service/killua-skateboards/orders/customers/5432341 

The URI could be a lot clearer. First, the customer ID is not a resource ID for a group of orders. It is a filtering criterion not a key to orders. Second, the URI implies that a single customer is returned. The last entity in the URI is customers. Customers followed by a resource ID for a customer entity should return a customer entity not a list of orders.

Proper ways to model this are the following:

GET http://sales-order-service/killua-skateboards/orders?$filter=customer-number eq 5432341 
GET http://sales-order-service/killua-skateboards/customers/543234/orders

The first URI above specifies that order entities would be returned. We are not using a resource ID but using a query parameter acting as a filter to which orders to return.

The second looks up the orders by looking up the customer first and uses the relationship between a customer and a customer’s orders to return the proper response.

Retrieving data through relationships

You can always use relationships between resources to filter down a particular hierarchy to a particular entity. Doing this is very easy to read and very clear on what you will be returning. Is it customers or orders returned? It’s very simple. It is always the last entity in the URI returned. I am first looking up a customer and from that customer going to the orders. I am returning orders.

GET http://sales-order-service/killua-skateboards/customers/543234/orders

Following this semantic will make sure that your URI is very easy to use and understand. I recently read a different article on this matter that promotes something similar to this following example.

Retrieve all customers who have orders.

GET http://sales-order-service/killua-skateboards/customers/orders/true

Can you quickly tell what above translates to in meaning? How about this?

GET http://sales-order-service/killua-skateboards/catalogs/clothes/shirts/colors/black

Are you retrieving the catalogs, clothes, shirts, or the color black? The URI above is very hard to understand and is not a clean implementation of a REST interface. Applying the rule that the last entity/resource mentioned is what the service returns makes the API much easier to understand.

GET http://sales-order-service/killua-skateboards/catalogs/clothes/shirts?$filter=color eq black

Retrieve shirts where the color is equal to black in the clothing catalog from the store Killua-skateboards. The URI is much simpler to understand. There is no doubt of what entity is returned.

Retrieve standard-skateboards where the color is equal to black in the skateboards catalog from the store Killua-skateboards.

GET http://sales-order-service/killua-skateboards/catalogs/skateboards/standard-skateboards?$filter=color eq black

So far we have seen where we relate entities together with a unique resource key. The unique key might be a surrogate key such as an order ID or it might be a natural key such as “skateboards”. The previous URI could be replaced to have a surrogate key that represents the natural key “skateboards” that works in conjunction with a company’s surrogate key that replaces the natural key “Killua-skateboards”. Both keys together work to point to a unique category of a single company.

Retrieve standards-skateboards where the color is black from the catalog with ID 1 from the company with the account number of 0980982.

GET http://sales-order-service/0980982/catalogs/1/standard-skateboards?$filter=color eq black

It is also possible to have numbers like this that are not necessarily surrogate keys but rather indexes. This URI is an example of retrieving the line-item that has the index of 1 in the given order.

GET http://sales-order-service/killua-skateboards/orders/984113422/line-items/1

Paging, Filtering, and Sorting

There are many ways these features occur implemented across the web. While I do not believe the OData specification is widely adopted as THE standard, I do like the semantics it provides for these features and choose to follow them. Some of the other OData standards, in my opinion, are too far from what is most commonly used and often not implemented by the REST frameworks available such as Spring REST and Sun Jersey.

Paging Example – Skip 100 entries and return top 10. The page size is equal to 10 and skip the first ten pages.

GET http://sales-order-service/orders?$top=10&$skip=100

Filtering Example – All orders where customer number equals 12345678.

GET http://sales-order-service/orders?$filter=customer-number eq 123456789

Filtering Example – All orders worth between $5000 and $8000.

GET http://sales-order-service/orders?$filter=total-cost ge 5000.00 and total-cost le 8000.00

Sorting Example – Order by order number

GET http://sales-order-service/orders?$top=10&$skip=100&$orderby=order-number desc

HTTP Status Codes

There are so many articles on HTTP status codes that I do not have much to add except to say, that in my experience, this part of REST services use cases has been the most neglected but one of the easiest to implement. That said, proper error handling and reporting most often is an afterthought. I would recommend before any upcoming REST service project that the team reviews the following list and what codes should be used for what success and error responses.

W3 HTTP Status Codes

Use Content-type and Accepted over /uri-path/.xml or .json

When requesting resources from a service, it is possible that service can send the representation of that resource in different formats such as XML and JSON. When a service supports more than one, it is standard and proper to let the service know which media types you as the client will accept by using the HTTP header “Accept”.

“Accept: application/xml” or “Accept: application/json”.

It is not uncommon for some services to allow for “.xml” or “.json” to be added to the end of the URI to specify the media accepted but this should be an extra feature, not the sole way of specifying it. It is useful to have for someone typing a URI in a browser, but it is not taking advantage of the standard semantics of the HTTP protocol.

GET http://sales-order-service/orders.json

When you are sending a representation to a service for a PUT or POST be sure to set the HTTP header “Content-Type”.

“Content-Type: application/xml” or “Content-Type: application/json”

Leave a Reply