6

I have REST API URL structure similar to:

/api/contacts                 GET          Returns an array of contacts
/api/contacts/:id             GET          Returns the contact with id of :id
/api/contacts                 POST         Adds a new contact and return it with an id added
/api/contacts/:id             PUT          Updates the contact with id of :id
/api/contacts/:id             PATCH        Partially updates the contact with id of :id
/api/contacts/:id             DELETE       Deletes the contact with id of :id

My question is about:

/api/contacts/:id             GET

Suppose that in addition to fetching the contact by ID, I also want to fetch it by an unique alias.

What should be URI structure be if I want to be able to fetch contact by either ID or Alias?

Jatinder Thind
  • 202
  • 2
  • 3
  • 11

4 Answers4

4

If you're alias's are not numeric i would suggest using the same URI structure and figuring out if it's an ID or an alias on your end. Just like Facebook does with username and user_id. facebook.com/user_id or facebook.com/username.

Another approach would be to have the client use GET /contacts with some extra GET parameters as filters to first search for a contact and then looking up the ID from that response.

Last option i think would be to use a structure like GET /contacts/alias/:alias. But this would kinda imply that alias is a subresource of contacts.

Stromgren
  • 1,064
  • 1
  • 16
  • 35
  • Good answer. But what would you suggest if the ID can not be distinguished from the alias in such a way? – shlomi33 Sep 03 '14 at 06:46
  • I don't see any other ways that wouldnt break the REST structure, except maybe doing something like GET /contacts/alias/:alias. But this would kinda imply that alias is a subresource of contacts – Stromgren Sep 03 '14 at 06:48
  • "But this would kinda imply that alias is a subresource of contacts." - Yes, but who need to know that? I mean only the API developers need that information to build links and router for the incoming messages. Everyone else will just use the links and decide about the navigation by checking the link relations... So in my opinion the IRI structure is irrelevant, and IRIs should not be part of your documentation. If they are ppl can write easy breaking clients which are building IRIs by themselves... – inf3rno Sep 04 '14 at 09:56
3

The path and query part of IRIs are up to you. The path is for hierarchical data, like api/version/module/collection/item/property, the query is for non-hierarchical data, like ?display-fields="id,name,etc..." or ?search="brown teddy bear"&offset=125&count=25, etc...

What you have to keep in mind, that you are working with resources and not operations. So the IRIs are resource identifiers, like DELETE /something, and not operation identifiers, like POST /something/delete. You don't have to follow any structure by IRIs, so for example you could use simply POST /dashuif328rgfiwa. The server would understand, but it would be much harder to write a router for this kind of IRIs, that's why we use nice IRIs.

What is important that a single IRI always belongs only to a single resource. So you cannot read cat properties with GET /cats/123 and write dog properties with PUT /cats/123. What ppl usually don't understand, that a single resource can have multiple IRIs, so for example /cats/123, /cats/name:kitty, /users/123/cats/kitty, cats/123?fields="id,name", etc... can belong to the same resource. Or if you want to give an IRI to a thing (the living cat, not the document which describes it), then you can use /cats/123#thing or /users/123#kitty, etc... You usually do that in RDF documents.

What should be URI structure be if I want to be able to fetch contact by either ID or Alias?

It can be /api/contacts/name:{name} for example /api/contacts/name:John, since it is clearly hierarchical. Or you can check if the param contains numeric or string in the /api/contacts/{param}.

You can use the query too, but I don't recommend that. For example the following IRI can have 2 separate meanings: /api/contacts?name="John". You want to list every contact with name John, or you want one exact contact. So you have to make some conventions about this kind of requests in the router of your server side application.

inf3rno
  • 24,976
  • 11
  • 115
  • 197
  • Thank you, /api/contacts/name:{name} looks very much feasible for what I wanted. Can you please point out any APIs which use similar approach? ___ Also, your comment about "that a single resource can have multiple IRIs", is this a good practice? I mean is it perfectly natural for a single resource to have multiple URIs? The examples you gave make sense, I just wanted to know whether this approach is not against best practices of REST APIs. – Jatinder Thind Sep 04 '14 at 06:05
  • It is just about the interpretation of different IRIs, for example `/users`, `/users?page=6`, `/users?search="Susanne"`. Now these are 3 different IRIs. So do we have here 3 different resources or just a single one? You can say, that `/users` is a collection resource, `/users?page=6` is a page resource and `/users?search="Susanne"` is a mapreduced collection resource, but that does not really makes sense for me. From my point of view `/users` is a collection resource, and all of the IRIs belong to that. Pagination and search is a typical usage of queries. – inf3rno Sep 04 '14 at 08:52
  • By pagination you can use range headers as an alternative. So `/users` `Content-Range: 26-50/1234`. This should return almost the same as `/users?offset=25&count=25`, except the next and previous links ofc. Now do the different pages belong to different resources? I don't think so. By range headers they belong to the same resource... Both range headers and query pagination params are REST best practices. For example by pagination: http://youtu.be/5WXYw4J4QOU?t=1h6s , but you can find a lot of articles about how to paginate by REST. – inf3rno Sep 04 '14 at 09:01
  • http://stackoverflow.com/questions/1929347/rest-multiple-uri-for-the-same-resource – inf3rno Sep 04 '14 at 09:03
1

I would consider adding a "search" resource when you are trying to resolve a resource with the alias:

GET /api/contacts/:id

and

GET /api/contacts?alias=:alias

or

GET /api/contacts/search?q=:alias
shlomi33
  • 1,458
  • 8
  • 9
1

First of all, the 'ID' in the URL doesn't have to be a numerical ID generated by your database. You could use any piece of data (including the alias) in the URL, as long as its unique. Of course, if you are using numerical ID's everywhere, it is more consistent to do the same in your contacts API. But you could choose to use the aliases instead of numeric IDs (as long as they are always unique).

Another approach would be, as Stromgren suggested, to allow both numeric IDs and aliases in the URL:

/api/contacts/123
/api/contacts/foobar

But this can obviously cause problems if aliases can be numeric, because then you wouldn't have any way to differentiate between an ID and a (numeric) alias.

Last but not least, you can implement a way of filtering the complete collection, as shlomi33 already suggested. I wouldn't introduce a search resource, as that isn't really RESTful, so I'd go for the other solution instead:

/api/contacts?alias=foobar

Which should return all contacts with foobar as alias. Since the alias should be unique, this will return 1 or 0 results.

Nic Wortel
  • 11,155
  • 6
  • 60
  • 79