Will
Will

Reputation: 773

URI naming hierarchy advice

I'm trying to build a Rest based service that has broad data coverage of sports data (results/fixtures/teams/players/leagues etc.) and associated statistics.

Coming up with a suitable URI hierarchy has me going around in circles at the moment. For example I could implement...

1) i) version/sports/league/season/entity

1) ii) version/entity/sports/league/season

1) iii) version/sports/entity/league/season

An example makes things slightly clearer (hopefully)...

Take a query that is to return all football players that are playing in the premier league in the 2010/2011 season...

2) i) 1.1/football/premier_league/2010_2011/players

2) ii) a) 1.1/players/football//premier_league/2010_2011

But not all sports (e.g. horse racing - horse and jockey, F1 etc.) really have players, so should players come after the sport?

2) ii) b) 1.1/horses/horse_racing/gold_cup/2010

2) iii) a) 1.1/football/players/premier_league/2010_2011

2) iii) b) 1.1/horse_racing/horses/gold_cup/2010

More example URIs below...

1.1/players/football/premier_league/2010_2011/teams/{id}
returns players in team for season?

1.1/players/football/premier_league returns players in the premier league?

1.1/results/football/premier_league/2010_2011/teams/{id} returns results for team season ?

also I envisage queries like...

1.1/football/premier_league/2010_2011/November/teams/{id}/results

with the month acting as a parameter to narrow the result set.

If the service had to include non-sporting entities, say political elections...there would be no players...

1.1/election/2012/parties/{id}/results
returns results for the political party

1.1/election/2012/parties/{id}/candidates returns the candidates associated with the party

or 1.1/results/election/2012/parties/{id} returns results for the political party

1.1/candidates/election/2012/parties/{id} returns the candidates associated with the party

Any advice on which hierarchy is better? I've tried searching this site and google, but the examples I've found have only covered trivial cases. I think I favour example 2-iii.

Thanks in advance.

Upvotes: 0

Views: 545

Answers (1)

Darrel Miller
Darrel Miller

Reputation: 142014

My advice would be to create identifiers for your entities that contain the minimum amount of information required. e.g.

/sport/{unqiueId}
/player/{unqiueId}
/horse/{unqiueId}
/team/{unqiueId}
/league/{unqiueId}
/season/{unqiueId}
/result/{unqiueId}

Note that I dropped version from the URL as I am firmly in the camp who believe versions don't belong in URLs.

One-to-one relationships can be handled by embedding links to entities in the response: e.g.

GET /player/234
=>
<Player Name="Kenny Dalglish">
   <Link rel="team" href="/team/732"/>
</Player>

One-to-Many relationships can be handled similarly but it requires creating a sub-resource of the entity resource.

GET /team/732
=>
<team Name="Liverpool FC">
   <Link rel="players" href="/team/732/players"/>
</Player>

Usually you will find all kind of scenarios where this pattern is useful:

/league/{unqiueId}/teams
/league/{unqiueId}/players
/season/{unqiueId}/teams
/league/{unqiueId}/seasons
/player/{unqiueId}/teams
/sport/{unqiueId}/teams
/sport/{unqiueId}/leagues
/sport/{unqiueId}/seasons

For representations that only contain subsets of data but are common queries they can easily be created using query strings and embedded into results:

GET /league/891
=>
<league Name="Premiership">
   <link rel="leaderboard" href="/league/891/teams?startposition=1&endposition=10"/>
   <link rel="currentseason" href="/season/972"/>
   <link rel="lastseason" href="/season/842"/>
   <link rel="sport" href="/sport/1"/>
   <link rel="teams" href="/teams?league=891/>
</league>

One of the interesting characteristics of this type of "api" is that instead of building a whole load of rules into your client application on how to build the url structure to make a request, the client application takes a different approach. The client needs to know the starting URL and the meaning of the rel attribute values. From there the client can just navigate to get what it wants. e.g.

Instead of the client building a request based on hardcoded knowledge of the server's hierarchy that looks like :

GET /Sport/Football/League/Premiership/Season/2011/Teams

instead it navigates it's way to the result like this:

GET /
=>
<SportData>
  <Link rel="sport football" href="/sport/1"/>  <!-- Client finds this link -->
  <Link rel="sport horseracing" href="/sport/2"/>
  ...
</SportData>

GET /sport/1
=>
<Sport Name="Football">
  <Link rel="league premiership" href="/league/891"/>  <!-- Client finds this link -->
  ...
</Sport>

GET /league/891
=>
<League Name="Premiership">
  <Link rel="season 2011" href="/season/2334"/>  <!-- Client finds this link -->
  ...
</League>


GET /season/2334
=>
<Season Name="2011">
  <Link rel="teams" href="/season/2334/teams"/>  <!-- Client finds this link -->
  ...
</Season>

I realize that this appears like a whole lot more work than the first option, however, it is not as bad as it looks. Using caching and bookmarks you can reduce the round trips considerably. The main benefit is that you can easily change the resources exposed by the service and introduce new relationships between resources without any effect on the client at all.

Ideally in a REST based system the only coupling between client and server is the root URL and the media-types that are sent back and forth. The client needs to know nothing about the URL space of the server.

Upvotes: 3

Related Questions