more on links

116 views
Skip to first unread message

Ning

unread,
Nov 8, 2012, 6:30:35 PM11/8/12
to json-...@googlegroups.com
I am trying to figure out how to use the links for rest APIs.

Suppose that I have a resource /departments and /departments/123 lets me access a particular department 123.

How will be the self link used by a consumer? say if I have:
"departments": {
    "name": "Department",
    "properties": {
      "departmentId": {
        "type": "integer",
        "description": "department identifier"
      },
      "departmentName": {
        "type": "string",
        "description": "department name"
      }
    }
    "links": [
      {
        "rel": "self",
        "href": "{departmentId}"
      }
    ]
  }

The self link is against a json instance, so I should already have the instance URI, which is /departments/123. What does the self link provide?

suman yadav

unread,
Nov 9, 2012, 1:20:16 AM11/9/12
to json-...@googlegroups.com
I think that's correct, the relative URI should be relative to the api base such that a client can make an ajax call with that piece of string to once again get this document, so in spirit anything that gets him back the document that he is on.

Geraint (David)

unread,
Nov 9, 2012, 4:18:39 AM11/9/12
to json-...@googlegroups.com
The "self" link is often not very useful if you have just fetched the document from that URI.  As you say, you already have the URI, so what does the "self" link provide?

However, it is more useful when you opened the data from somewhere else, and do not know the URI for it.  In that case, the "self" link is just a way of specifying that URI from the item data.

It's also useful when you have composite data.  For instance, say we have this document fetched from /rooms/4:
{
    "roomId": 4,
    "name": "The lobby",
    "furniture": [
        {
            "furnitureId": 3563,
            "roomId": 4,
            "name": "Receptionist's desk",
            "type": "desk",
            ...
        },
        {
            "furnitureId": 2031,
            "roomId": 4,
            "name": "A wheely chair",
            "type": "chair",
            ...
        },
    ]
}

This document contains the full details of all the furniture in that room, bundled up inside it.  However, if I want to interact with a particular piece of furniture, how do I do that?  I could use a JSON Pointer fragment, but then if I moved a piece of furniture from one room to the other, nobody else could tell it was the same piece of furniture.

So I define a "self" link on the furniture items:
{
    "title": "Room",
    "type": "object",
    "properties": {
        "furniture": {
            "type": "array",
            "items": {
                "title": "Furniture",
                "type": "object",
                "properties": { ... },
                "links": [
                    {
                        "rel": "self",
                        "href": "/furniture/{furnitureId}"
                    },
                    {
                        "rel": "parent",
                        "href": "/room/{roomId}"
                    }
                ]
            }
        }
    }
}

Now, if I want to move the wheely chair to another room, I don't have to delete the one in the lobby, and create a new one somewhere else, because this chair has a unique URI of its own.  And what's more, I know this URI, even though it was served up to me embedded inside another document.

So I can probably just modify the chair itself using a PUT request:
PUT /furniture/2031
{
    "furnitureId": 2031,
    "roomId": 15,
    "name": "The wheely chair from the lobby",
    "type": "chair",
    ...
}

Presumably, the server would at this point stop including the chair in the furniture listing for the lobby (#4), and start including it in a different room (#15).

Geraint (David)

unread,
Nov 9, 2012, 4:22:20 AM11/9/12
to json-...@googlegroups.com
A couple of things worth noting:  The client is under no obligation to believe that the data for the furniture items at the same as the data from the claimed URI.  It is a useful hint.

Also, the "self" link is only used because the complete data for the items of furniture is included, so the client only needs to make one request.  If you only included a small portion of it, then a "full" link would be more appropriate, indicating that the client should visit that URI to get the rest of the data.

Ning

unread,
Nov 9, 2012, 3:27:30 PM11/9/12
to json-...@googlegroups.com
Thanks for the clarification.

1. Is it possible to reference the property from the parent schema inside a nested schema's link? Take my previous example, if I expand it:
"departments": {
    "name": "Department",
    "properties": {
      "departmentId": ...,
      "departmentName": ...,
      "locationId": {
        "type": integer
      },
      "employees": {
        "items": {
          "properties": {...},
          "links": [
            {"rel": "location", 
             "href": "/locations/{locationId}},
          ]
        }
      }
    }
  }
Is the location link valid?

2. Regarding the "instances" link. Using the above example, I can have a link: 
{"rel": "instances",
 "href": "/departments"}
for the department schema, and GET /departments is supposed to return a list of departments. Is it right?

Thanks.


Ning

Geraint (David)

unread,
Nov 10, 2012, 7:34:40 AM11/10/12
to json-...@googlegroups.com
1. No - you can only reference the immediate properties of an object.

2. According to the version 3 specification, yes.  I'm not sure I like it, though - to me, schemas should describe items.  However, that's how draft version 3 was written, and I'm not going to try and change it if people find it useful.

Ning

unread,
Nov 12, 2012, 6:56:26 PM11/12/12
to json-...@googlegroups.com
Thanks David.

I am struggling with how I handle parent child, such as order header, line, and shipment. An order line cannot exist outside a header. So we want to make the URI something like:
/orders/123/lines/1/shipments/1

In the schema, I want to do something like this:

"orders": {
  "properties": {
    "orderId":...
    "lines": {
      "items": {
        "properties": {
          "lineId":...
          "shipments": {
             "items": {
               "properties" {"shipmentId":...},
               "links": [
                 {"rel": "self", "href": "/orders/{orderId}/lines/{lineId}/shipments/{shipmentId}"}
               ]
             }
           }
        },
        "links": [
           {"rel": "self", "href": "/orders/{orderId}/lines/{lineId}"}
        ]
      }
    }
  },
  "links":[
    {
      "rel": "self", "href": "/orders/{orderId}"
    }
   ]
}

but this is not supported. Is there any alternative?
Can I reference the parent schema's self link from a child's schema?

Geraint (David)

unread,
Nov 13, 2012, 5:23:51 AM11/13/12
to json-...@googlegroups.com
OK - say you access a shipment via its URL, from the URI: /orders/123/lines/1/shipments/1
{
    "shipmentId": 1,
    ...
}

There is no information in that object about the order or line it belongs to.  It's just a shipment ID.  At this point, you might as well just list it as /shipments/12345 instead.

What is the benefit of including the order or the lines in the URI?  It would be incredibly bad style for a client to inspect the URI and extract the order/line.

Geraint (David)

unread,
Nov 13, 2012, 5:49:50 AM11/13/12
to json-...@googlegroups.com
It sounds like what you want, however, is to be able to inspect a shipment and see what line or order it came from.  At this point, I can see two options:

Option 1:
You include the orderId and lineId inside every shipment object, and the orderID inside the line objects.  This makes your data bigger, and more redundant, but gives you what you want - you can define the "self" and "parent" links using these IDs.

Option 2:
You instead address the shipments by JSON Pointer fragment.  So if I wanted to access the first shipment of the first line in an order, the correct URI would be: /orders/123#/lines/0/shipments/0  - This also means that you no longer need to assign unique line IDs or shipment IDs.  Every shipment would be identified by its position in the line, and that line's position in the order header.

One disadvantage of option 2 is that you can no longer download the shipments individually.  In order to look up the above data, the client would have to download the whole of /orders/123, and use the fragment part to locate the appropriate shipment.  However, once it's done that, it can do things like "look at the parent object of this shipment" instead of relying on a parent link.

Option 3:
The third option is what I described previously - change the URL so that lines are at /lines/123 and shipments are at /shipments/123.

My opinion:
Personally, I prefer option 1, for a couple of reasons:
  • You can fetch the shipments or lines individually (i.e. without downloading the rest of the order)
  • It allows more intuitive numbering of lines.  For human-friendliness, the first line in each header should be numbered 1 or 0.  But that means that lineId is not a unique key!  However, if you include the orderId in the line object, then the two keys together form a unique key (orderId, lineId) which can be derived from the object.
  • The order header object, and the line object are composite items - they contain lots of other documents (with their own URIs) inside themselves.  I'm therefore more comfortable with redundancy here, because the redundancy is "auto-generated" by the server, not stored - there is therefore no issue of maintaining data integrity.
  • It still keeps the URL scheme, which I like - it gives space for intuitive listings like /orders/123/lines/1/shipments/ to see all shipments in that line.

Another disadvantage is that you can't (for example) insert a new line at the beginning of the order without messing everything up.

Geraint (David)

unread,
Nov 13, 2012, 5:55:46 AM11/13/12
to json-...@googlegroups.com
I apparently can't count - that was three options.

"Our two weapons are fear and surprise...and ruthless efficiency"


On Monday, 12 November 2012 23:56:26 UTC, Ning wrote:

Geraint (David)

unread,
Nov 13, 2012, 6:09:21 AM11/13/12
to json-...@googlegroups.com
"Another disadvantage is that you can't (for example) insert a new line at the beginning of the order without messing everything up."

Wow, my formatting is all over the place today - that was referring to Option #2.

Ning

unread,
Nov 13, 2012, 2:09:43 PM11/13/12
to json-...@googlegroups.com
Thanks for helping me with this.

Is it possible to represent the instances links for lines and shipments? It needs the parent id in the link, but I don't think we can use that kind of URI templating there.


Ning

Geraint (David)

unread,
Nov 13, 2012, 2:19:09 PM11/13/12
to json-...@googlegroups.com
An "instances" link probably isn't how I would do this.  That's more a service-discovery kind of thing.

However, you could make a more explicit link in the order/line objects - you can provide a "hint" schema which can tell the client what kind of data it might expect at a particular URL (much like the way "instances" links work).  The one for lines might look like this (defined on the order object):
{
    ...
    "links": [
        {
            "href": "/orders/{orderId}/lines/",
            "rel": "whatever",
            "targetSchema": {
                "description": "List of lines",
                "type": "array",
                "items": {"$ref": "/uri/of/your/line/schema"}
            }
        }
    ]
Reply all
Reply to author
Forward
0 new messages