Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Design RESTful API using HAL - serialize model relationships

I'm relatively new to REST but I've been doing my homework on how RESTful should be. Now I'm trying to create a RESTful api implementing a JSON+HAL serializer for my models which have relationships with other models.
Example models in python:

class Category(Model):
    name = CharField()
    parent = ManyToOneField(Category)
    categories = OneToManyField(Category)
    products = ManyToManyField(Product)

class Product(Model):
    name = CharField()
    price = DecimalField()
    related = ManyToManyField(Product)
    categories = ManyToManyField(Category)

lets suppose we have a category "catalog" with a sub-category "food" with products "burger" and "hot-dog" which are both related.
First question. Categories and products should be resources so they need an URI, should I implement an uri field in my model and store it in the DB or somehow calculate it at runtime, what about multiple identifiers(URIs)?
Second question. Discoverability, In Hal format what should "GET /" and diferent nodes return to make the api easily self discoverable.

{
  "_links":{
    "self":{
      "href":"/"
    },
    "categories":[
      {
        "href":"/catalog"
      }
    ]
  }
}

Third question. Add as properties, embed or link. Example "GET /catalog/food":

{
  "_links":{
    "self":{
      "href":"/catalog/food"
    }
  },
  "name":"food",
  "parent":"/catalog",
  "categories":[],
  "products":[
    "/products/burger",
    "/products/hot-dog"
  ]
}

{
  "_links":{
    "self":{
      "href":"/catalog/food"
    },
    "parent":{
      "href":"/catalog"
    },
    "categories":[

    ],
    "products":[
      {
        "href":"/products/burger"
      },
      {
        "href":"/products/hot-dog"
      }
    ]
  },
  "name":"food"
}

{
  "_links":{
    "self":{
      "href":"/catalog/food"
    }
  },
  "name":"food",
  "_embedded":{
    "parent":{
      "_links":{
        "self":{
          "href":"/catalog"
        }
      },
      "name":"catalog",
      ...
    },
    "categories":[

    ],
    "products":[
      {
        "_links":{
          "self":{
            "href":"/products/burger"
          }
        },
        "name":"burger",
        ...
      },
      {
        "_links":{
          "self":{
            "href":"/products/hot-dog"
          }
        },
        "name":"hot-dog",
        ...
      }
    ]
  }
}

Fourth question. How deep should I go when returning structures. Example "GET /catalog

{
  "_links":{
    "self":{
      "href":"/catalog"
    }
  },
  "name":"catalog",
  "parent":null,
  "categories":[
    {
      "name":"food",
      "parent":{...},
      "categories":[],
      "products":[
        {
          "name":"burger",
          "price":"",
          "categories":[...],
          "related":[...]
        },
        {
          "name":"hot-dog",
          "price":"",
          "categories":[...],
          "related":[...]
        }
      ]
    }
  ],
  "products": []
}
like image 224
olanod Avatar asked Apr 12 '13 23:04

olanod


People also ask

How to design a REST API based application?

1 Identify Object Model. The very first step in designing a REST API based application is – identifying the objects which will be presented as resources. 2 Create Model URIs. Now when the object model is ready, it’s time to decide the resource URIs. ... 3 Determine Representations. ... 4 Assign HTTP Methods. ... 5 More Actions. ...

What are resources in RESTful API design?

As we have seen in Resources, the resource is the fundamental unit in RESTful API design. Resources model objects from the application data model. Resources do not exist in isolation, but have relationships to other resources.

How to model the parameters of a rest resource?

If the REST Resource uses a POST, PUT, PATCH or DELETE method and if parameter is required in using the REST Resource, model the parameters by drawing class (es). Move your mouse pointer over the REST Request Body icon.

What is the default Serializer for the URL field in Salesforce?

The url field will be represented using a HyperlinkedIdentityField serializer field, and any relationships on the model will be represented using a HyperlinkedRelatedField serializer field. By default, all the model fields on the class will be mapped to a corresponding serializer fields.


2 Answers

About 1st question: I wouldn't store the URIs in the DB. You can easily calculate them inside your controller in runtime, and it's of controller's responsibility to care about URIs. This way you keep your model and your API decoupled, and should you decide to change the API structure in the future, you won't need to update your whole database with the new URIs.

About multiple identifiers, I'm not sure what the question is, but again, in my opinion, it has nothing to do with the DB, it's the routing and the controllers who should care about how to deal with any URIs.

About 2nd question: First of all, as a side note: I would keep the word categories as part of the URI. For example, I 'd have http://domain.com/api/categories/catalog/food. This way, you make your API more descriptive and more "hackable", meaning that user should be able to remove the /catalog/food part and expect to receive a collection with all the available categories.

Now, about what GET should return to allow discoverability: I think it's already being made clear from your URI structure. When user hits GET /categories he expects to get a list with the categories (the name and URI for each, to keep it lightweight), and when he follows one of the URIs like GET /categories/catalog he should receive the resource catalog which is a category. Likewise, when he wants to GET /products/burger, he should receive a product resource with all the attributes you have in your model. You may want to check this example about the structure of your responses.

About 3rd question: Again, the same example can help you form the structure. I think your 2nd way of response is closer to that, but I would also add a name field, not only the href.

About 4th question: When the GET request expects a collection of resources (like GET /categories) I would suggest providing only the necessary for each resource, that is, the name and the URI for each and only when user follows the desired URI, he can receive the rest information.

In your example, catalog is a resource, so on GET /categories/catalog I would include of course the name of the resource (catalog) and its self link, and for parent, sub-categories and products that are related to it, I would just provide the name and the URI for each, to keep it light. But: This was a general thought about designing APIs. In your actual problem, you should decide depending on your specific business problem. I mean, if your API is about a restaurant menu with categories and dishes, you may want to include the price, or a small description even when responding not for the actual product but for a collection of products, because probably for your users, that's an important information. So generally, provide all the necessary info (you only know what are these for your problem) when responding about a list of resources, and provide all the details of the resource when responding about a specific resource.

like image 155
zafeiris.m Avatar answered Oct 12 '22 17:10

zafeiris.m


  1. I would store something in the DB and calculate the URI at Runtime. That way if you move boxes it's not static.
  2. Create a 'bookmark' page. The page we created was just a list of links with their rels. I believe HAL defines that specifically. The bookmark page was the only page other pages needed to know about
  3. Not sure about this
  4. How deep you go is up to you. There is a big debate now at my place of work for fine grain vs course grain. I'm going to do fine grain with small resource to keep the api simple, but then use the Expand-ability concept. It's a combination ofthe idea of composite resources defined on pg 35 of Subbu’s REST book and the expand concept used by Netflix. http://developer.netflix.com/docs/REST_API_Conventions
like image 30
suing Avatar answered Oct 12 '22 16:10

suing