Tuesday, January 24, 2012

RESTful standard resolved! - part I

Latly I'm trying to build a web application which will be exposed in a RESTfull manner.
There are some general guideline and hints about how to define it, but no explicit standard or accepted schema structure to use.

After reading some info on the web, I think I manage to crack the pattern :-)
I would like to share the rules and structure I formed and hopefully to get some feedback about it and improve it, so please don't hesitate to leave notes and point any pain point that structure has.

The high level pattern is:
http(s)://server.com/app-name/{version}/{domain}/{rest-convention}

Where {version} is the api version this interface work with and {domain} is an area you wish to define for any technical (e.g. security - allow certain users to access that domain) or business reason (e.g. gather functionality under same prefix).

The {rest-convention} denotes the set of REST API which is available under that domain.
It has the following convention:

  • singular-resourceX/
    • URL example: order/  (order is the singular resource X)
      • GET - will return a new order
      • POST - will create a new order. values are taken from the post content body.
  • singular-resourceX/{id} 
    • URL example: order/1 (order is the singular resource X)
      • GET - will return an order with the id 1
      • DELETE - will delete an order with the id 1
      • PUT - will update an order with the id 1.  Order values to update are taken from the post content body.

  • plural-resourceX/
    • URL example: orders/
      • GET - will return all orders
  • plural-resourceX/search
    • URL example: orders/search?name=123
      • GET - will return all orders that answer search criteria (QBE, no join) -order  name equal to 123
  • plural-resourceX/searchByXXX
    • URL example: orders/searchByItems?name=ipad
      • GET - will return all orders that answer the customized query - get all orders  that associated to items with name ipad
  • singular-resourceX/{id}/pluralY
    • URL example: order/1/items/ (order is the singular resource X, items is the plural resource Y)
      • GET - will return all items that associated to order #1
  •  singular-resourceX/{id}/singular-resourceY/
    • URL example: order/1/item/
      • GET - return a new item (transient) that is associated order #1
      • POST - create a new item and associate it to order  #1. Item values are taken from the post content body.
  •  singular-resourceX/{id}/singular-resourceY/{id}/singular-resourceZ/
    • URL example: order/1/item/2/package/
      • GET - return a new package (transient) that is associated to item 2 (i.e. how to pack the item) and is associated to order #1
      • POST - create a new package and associate it to item #2 & order #1. package values are taken from the post content body.

One basically can have further nesting as long as the above convention is maintained and no plural resource is defined after another plural resource.
There are further guidelines/notes to make things clear:
- When using plural resource, the returning instances will be those of the last plural resource used.
- When using singular resource the returning instance will be the last singular resource used.
- On search, the returning instances will be those of the last plural entity used.

Hopefully your insight will help me improve this structure and overcome issues which you might came across.

In next post, after this suggested structure will be improved, I will try to give technical examples how to implement it using Spring MVC 3.1


14 comments :

  1. You should have something like

    http(s)://server.com/app-name/{version}/{domain}/{rest-convention}

    where {version} is the version of the protocol. Suppose you have a new field on order, if you update the protocol, you should not break backward compatibility of older clients

    ReplyDelete
    Replies
    1. I like the version suggestion very much.
      I'll update the structure.

      Delete
  2. You can always user the Request Headers for the Version.

    ReplyDelete
    Replies
    1. Using the Header, might harm the principle of easy discovery.
      Moreover it might be not so trivial for non technical users and might introduce complexity to this simple protocol.

      Delete
  3. Like it ...
    Only one question that I'd like to hear your opinion on. And it might come up when you talk about the tech side with spring.

    How do you handle updates? or state changes i.e.

    Say I have an order, I can do a PUT to update the order, and before its processed I can change whatever I want. But once its been shipped, the set of things that a PUT request can do has changed. Do you have any patterns for handling the available actions based on the state of an object?

    ReplyDelete
  4. Hello, I like all of this except for the /search and /searchByItems URI components. The "?" delimiter should be sufficient to indicate a search by parameter-value pairs. Using "/search" puts a verb where a noun belongs: the only verbs in REST are the HTTP methods GET / POST / PUT / DELETE, etc.

    To search for a resource by one of its sub-resources, could the parameter name be specified in this way?

    orders?item.name=iPad

    ReplyDelete
    Replies
    1. That was my one issue as well.. is "search" a resource? It's simpler to treat any search as a parameterised GET of the resource collection you are after (think of it as a filter)

      Delete
  5. Does it mean order/ GET will create and return a new (default) order?

    ReplyDelete
    Replies
    1. It will not create it in the DB. It will return a new instance w/o an id.

      Delete
  6. This comment has been removed by the author.

    ReplyDelete
  7. Respectfully, your URL structure is all wrong. URLs are supposed to describe resources, the THINGS your application is interested in. Nouns, not verbs.

    http://server.com/app-name/{version}/{domain}/{rest-convention}

    app-name is not a resource. I might compromise here to avoid naming conflicts.

    version is not a resource. Versioning is better accomplished by versioning MIME types. Just do a Web search on REST versioning and you'll see plenty of discussion about this.

    domain is not a resource.

    orders/search?name=123

    search is not a resource.
    Better is /orders?name=123

    orders/searchByItems?name=ipad
    More difficult. I would probably use
    /orders?itemName=123. If your app has complex search requirements, perhaps
    orders?criteria="A complex search string"

    ReplyDelete
    Replies
    1. Hi Douglass,

      Thanks for the comment. Been waiting for someone with some concerns about the suggested pattern.

      Regarding your comment - first - I think that pure REST is not a silver bullet and complex business app need to have some adjustment, while sticking as much as possible to the REST philosophy.

      * version- URL vs. Header: I took google approach and put version in the URL. IMHO it is more discoverable for humans and easier to use as API for developers.
      * domain is for technical reasons (such as security simplification) and provide additional technical flexibility. Till now, the URL has nothing to do with REST. REST standard starting after the domain section.

      * Complex queries - that's where the fun stuff starts and the REST deviation would probably take place: Assuming each resource has multiple complex queries, some with overlapping parameters, in your approach the server would have impossible task to know which one of the queries the client refer to. Therefore you have to explicitly differentiate it.

      Let me know if you have other concerns. Thanks!

      Delete
  8. Way late comment...

    *API* versions do not belong in *resource* identifiers. Share resources across API versions. Use media types for data formats. If you google RESTful Versioning, you'll find lots of explanation for why URI based versioning is a rookie mistake.

    Usually, people put individual resources under a plural identifier, as if it is a folder in the file system. So /orders/1 is a specific order and /orders?name={foo} is a collection of hits. I can create an order via a POST to /orders. That said, there is nothing wrong or unRESTful with doing it your way.

    I partially agree with Douglass above that /orders?name=123 is a succinct and clear syntax. But there is nothing wrong with including application IF you will never share resources across applications. But it's more idiomatic to use the hostname for this.

    I disagree with him that putting domain in for a security or business reason is wrong. The path part of a URI is for hierarchical organization of resources. If you have business unit's bu-A and bu-B, it is completely reasonable to have /bu-A/order/1 and /bu-B/order/2. REST does not require you to have a single path for the same "TYPE" of resource and actually discourages clients from expecting all resources to have matching paths.

    I tend to agree that adding "search" in the hierarchy is needless. But it isn't WRONG, provided you are actually following HATEOAS and building up these URIs for your clients instead of having them construct an RPC call by binding in the arguments they way.

    ReplyDelete